| /* |
| * Copyright (C) 2011-2020 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #pragma once |
| |
| #if ENABLE(DFG_JIT) |
| |
| #include "CCallHelpers.h" |
| #include "CodeBlock.h" |
| #include "DFGDisassembler.h" |
| #include "DFGGraph.h" |
| #include "DFGInlineCacheWrapper.h" |
| #include "DFGJITCode.h" |
| #include "DFGOSRExitCompilationInfo.h" |
| #include "GPRInfo.h" |
| #include "HandlerInfo.h" |
| #include "JITCode.h" |
| #include "JITInlineCacheGenerator.h" |
| #include "LinkBuffer.h" |
| #include "MacroAssembler.h" |
| #include "PCToCodeOriginMap.h" |
| |
| namespace JSC { |
| |
| class AbstractSamplingCounter; |
| class CodeBlock; |
| class VM; |
| |
| namespace DFG { |
| |
| class JITCodeGenerator; |
| class NodeToRegisterMap; |
| class OSRExitJumpPlaceholder; |
| class SlowPathGenerator; |
| class SpeculativeJIT; |
| class SpeculationRecovery; |
| |
| struct EntryLocation; |
| struct OSRExit; |
| |
| // === CallLinkRecord === |
| // |
| // A record of a call out from JIT code that needs linking to a helper function. |
| // Every CallLinkRecord contains a reference to the call instruction & the function |
| // that it needs to be linked to. |
| struct CallLinkRecord { |
| CallLinkRecord(MacroAssembler::Call call, FunctionPtr<OperationPtrTag> function) |
| : m_call(call) |
| , m_function(function) |
| { |
| } |
| |
| MacroAssembler::Call m_call; |
| FunctionPtr<OperationPtrTag> m_function; |
| }; |
| |
| // === JITCompiler === |
| // |
| // DFG::JITCompiler is responsible for generating JIT code from the dataflow graph. |
| // It does so by delegating to the speculative & non-speculative JITs, which |
| // generate to a MacroAssembler (which the JITCompiler owns through an inheritance |
| // relationship). The JITCompiler holds references to information required during |
| // compilation, and also records information used in linking (e.g. a list of all |
| // call to be linked). |
| class JITCompiler : public CCallHelpers { |
| public: |
| friend class SpeculativeJIT; |
| |
| JITCompiler(Graph& dfg); |
| ~JITCompiler(); |
| |
| void compile(); |
| void compileFunction(); |
| |
| // Accessors for properties. |
| Graph& graph() { return m_graph; } |
| |
| // Methods to set labels for the disassembler. |
| void setStartOfCode() |
| { |
| m_pcToCodeOriginMapBuilder.appendItem(labelIgnoringWatchpoints(), CodeOrigin(BytecodeIndex(0))); |
| if (LIKELY(!m_disassembler)) |
| return; |
| m_disassembler->setStartOfCode(labelIgnoringWatchpoints()); |
| } |
| |
| void setForBlockIndex(BlockIndex blockIndex) |
| { |
| if (LIKELY(!m_disassembler)) |
| return; |
| m_disassembler->setForBlockIndex(blockIndex, labelIgnoringWatchpoints()); |
| } |
| |
| void setForNode(Node* node) |
| { |
| if (LIKELY(!m_disassembler)) |
| return; |
| m_disassembler->setForNode(node, labelIgnoringWatchpoints()); |
| } |
| |
| void setEndOfMainPath(); |
| void setEndOfCode(); |
| |
| CallSiteIndex addCallSite(CodeOrigin codeOrigin) |
| { |
| return m_jitCode->common.codeOrigins->addCodeOrigin(codeOrigin); |
| } |
| |
| CallSiteIndex emitStoreCodeOrigin(CodeOrigin codeOrigin) |
| { |
| CallSiteIndex callSite = addCallSite(codeOrigin); |
| emitStoreCallSiteIndex(callSite); |
| return callSite; |
| } |
| |
| void emitStoreCallSiteIndex(CallSiteIndex callSite) |
| { |
| store32(TrustedImm32(callSite.bits()), tagFor(CallFrameSlot::argumentCountIncludingThis)); |
| } |
| |
| // Add a call out from JIT code, without an exception check. |
| Call appendCall(const FunctionPtr<CFunctionPtrTag> function) |
| { |
| Call functionCall = call(OperationPtrTag); |
| m_calls.append(CallLinkRecord(functionCall, function.retagged<OperationPtrTag>())); |
| return functionCall; |
| } |
| |
| Call appendOperationCall(const FunctionPtr<OperationPtrTag> function) |
| { |
| Call functionCall = call(OperationPtrTag); |
| m_calls.append(CallLinkRecord(functionCall, function)); |
| return functionCall; |
| } |
| |
| void appendCall(CCallHelpers::Address address) |
| { |
| call(address, OperationPtrTag); |
| } |
| |
| void exceptionCheck(); |
| |
| void exceptionCheckWithCallFrameRollback() |
| { |
| m_exceptionChecksWithCallFrameRollback.append(emitExceptionCheck(vm())); |
| } |
| |
| OSRExitCompilationInfo& appendExitInfo(MacroAssembler::JumpList jumpsToFail = MacroAssembler::JumpList()) |
| { |
| OSRExitCompilationInfo info; |
| info.m_failureJumps = jumpsToFail; |
| m_exitCompilationInfo.append(info); |
| return m_exitCompilationInfo.last(); |
| } |
| |
| #if USE(JSVALUE32_64) |
| void* addressOfDoubleConstant(Node*); |
| #endif |
| |
| void addGetById(const JITGetByIdGenerator& gen, SlowPathGenerator* slowPath) |
| { |
| m_getByIds.append(InlineCacheWrapper<JITGetByIdGenerator>(gen, slowPath)); |
| } |
| |
| void addGetByIdWithThis(const JITGetByIdWithThisGenerator& gen, SlowPathGenerator* slowPath) |
| { |
| m_getByIdsWithThis.append(InlineCacheWrapper<JITGetByIdWithThisGenerator>(gen, slowPath)); |
| } |
| |
| void addGetByVal(const JITGetByValGenerator& gen, SlowPathGenerator* slowPath) |
| { |
| m_getByVals.append(InlineCacheWrapper<JITGetByValGenerator>(gen, slowPath)); |
| } |
| |
| void addPutById(const JITPutByIdGenerator& gen, SlowPathGenerator* slowPath) |
| { |
| m_putByIds.append(InlineCacheWrapper<JITPutByIdGenerator>(gen, slowPath)); |
| } |
| |
| void addPutByVal(const JITPutByValGenerator& gen, SlowPathGenerator* slowPath) |
| { |
| m_putByVals.append(InlineCacheWrapper<JITPutByValGenerator>(gen, slowPath)); |
| } |
| |
| void addDelById(const JITDelByIdGenerator& gen, SlowPathGenerator* slowPath) |
| { |
| m_delByIds.append(InlineCacheWrapper<JITDelByIdGenerator>(gen, slowPath)); |
| } |
| |
| void addDelByVal(const JITDelByValGenerator& gen, SlowPathGenerator* slowPath) |
| { |
| m_delByVals.append(InlineCacheWrapper<JITDelByValGenerator>(gen, slowPath)); |
| } |
| |
| void addInstanceOf(const JITInstanceOfGenerator& gen, SlowPathGenerator* slowPath) |
| { |
| m_instanceOfs.append(InlineCacheWrapper<JITInstanceOfGenerator>(gen, slowPath)); |
| } |
| |
| void addInById(const JITInByIdGenerator& gen, SlowPathGenerator* slowPath) |
| { |
| m_inByIds.append(InlineCacheWrapper<JITInByIdGenerator>(gen, slowPath)); |
| } |
| |
| void addInByVal(const JITInByValGenerator& gen, SlowPathGenerator* slowPath) |
| { |
| m_inByVals.append(InlineCacheWrapper<JITInByValGenerator>(gen, slowPath)); |
| } |
| |
| void addPrivateBrandAccess(const JITPrivateBrandAccessGenerator& gen, SlowPathGenerator* slowPath) |
| { |
| m_privateBrandAccesses.append(InlineCacheWrapper<JITPrivateBrandAccessGenerator>(gen, slowPath)); |
| } |
| |
| void addJSCall(Label slowPathStart, Label doneLocation, OptimizingCallLinkInfo* info) |
| { |
| m_jsCalls.append(JSCallRecord(slowPathStart, doneLocation, info)); |
| } |
| |
| void addJSDirectCall(Label slowPath, OptimizingCallLinkInfo* info) |
| { |
| m_jsDirectCalls.append(JSDirectCallRecord(slowPath, info)); |
| } |
| |
| void addWeakReference(JSCell* target) |
| { |
| m_graph.m_plan.weakReferences().addLazily(target); |
| } |
| |
| void addWeakReferences(const StructureSet& structureSet) |
| { |
| for (unsigned i = structureSet.size(); i--;) |
| addWeakReference(structureSet[i]); |
| } |
| |
| template<typename T> |
| Jump branchWeakStructure(RelationalCondition cond, T left, RegisteredStructure weakStructure) |
| { |
| Structure* structure = weakStructure.get(); |
| #if USE(JSVALUE64) |
| Jump result = branch32(cond, left, TrustedImm32(structure->id().bits())); |
| return result; |
| #else |
| return branchPtr(cond, left, TrustedImmPtr(structure)); |
| #endif |
| } |
| |
| void noticeOSREntry(BasicBlock&, JITCompiler::Label blockHead, LinkBuffer&); |
| void noticeCatchEntrypoint(BasicBlock&, JITCompiler::Label blockHead, LinkBuffer&, Vector<FlushFormat>&& argumentFormats); |
| |
| unsigned appendOSRExit(OSRExit&& exit) |
| { |
| unsigned result = m_osrExit.size(); |
| m_osrExit.append(WTFMove(exit)); |
| return result; |
| } |
| |
| unsigned appendSpeculationRecovery(const SpeculationRecovery& recovery) |
| { |
| unsigned result = m_speculationRecovery.size(); |
| m_speculationRecovery.append(recovery); |
| return result; |
| } |
| |
| RefPtr<JITCode> jitCode() { return m_jitCode; } |
| |
| Vector<Label>& blockHeads() { return m_blockHeads; } |
| |
| CallSiteIndex recordCallSiteAndGenerateExceptionHandlingOSRExitIfNeeded(const CodeOrigin&, unsigned eventStreamIndex); |
| |
| PCToCodeOriginMapBuilder& pcToCodeOriginMapBuilder() { return m_pcToCodeOriginMapBuilder; } |
| |
| VM& vm() { return m_graph.m_vm; } |
| |
| void emitRestoreCalleeSaves() |
| { |
| emitRestoreCalleeSavesFor(&RegisterAtOffsetList::dfgCalleeSaveRegisters()); |
| } |
| |
| void emitSaveCalleeSaves() |
| { |
| emitSaveCalleeSavesFor(&RegisterAtOffsetList::dfgCalleeSaveRegisters()); |
| } |
| |
| class LinkableConstant final : public CCallHelpers::ConstantMaterializer { |
| public: |
| enum NonCellTag { NonCell }; |
| LinkableConstant() = default; |
| LinkableConstant(JITCompiler&, JSCell*); |
| LinkableConstant(LinkerIR::Constant index) |
| : m_index(index) |
| { } |
| |
| void materialize(CCallHelpers&, GPRReg); |
| void store(CCallHelpers&, CCallHelpers::Address); |
| |
| template<typename T> |
| static LinkableConstant nonCellPointer(JITCompiler& jit, T* pointer) |
| { |
| static_assert(!std::is_base_of_v<JSCell, T>); |
| return LinkableConstant(jit, pointer, NonCell); |
| } |
| |
| static LinkableConstant structure(JITCompiler& jit, RegisteredStructure structure) |
| { |
| return LinkableConstant(jit, structure.get()); |
| } |
| |
| bool isUnlinked() const { return m_index != UINT_MAX; } |
| |
| void* pointer() const { return m_pointer; } |
| LinkerIR::Constant index() const { return m_index; } |
| |
| #if USE(JSVALUE64) |
| Address unlinkedAddress() |
| { |
| ASSERT(isUnlinked()); |
| return Address(GPRInfo::constantsRegister, JITData::offsetOfData() + sizeof(void*) * m_index); |
| } |
| #endif |
| |
| private: |
| LinkableConstant(JITCompiler&, void*, NonCellTag); |
| |
| LinkerIR::Constant m_index { UINT_MAX }; |
| void* m_pointer { nullptr }; |
| }; |
| |
| void loadConstant(LinkerIR::Constant, GPRReg); |
| void loadLinkableConstant(LinkableConstant, GPRReg); |
| void storeLinkableConstant(LinkableConstant, Address); |
| |
| Jump branchLinkableConstant(RelationalCondition cond, GPRReg left, LinkableConstant constant) |
| { |
| #if USE(JSVALUE64) |
| if (constant.isUnlinked()) |
| return CCallHelpers::branchPtr(cond, left, constant.unlinkedAddress()); |
| #endif |
| return CCallHelpers::branchPtr(cond, left, CCallHelpers::TrustedImmPtr(constant.pointer())); |
| } |
| |
| Jump branchLinkableConstant(RelationalCondition cond, Address left, LinkableConstant constant) |
| { |
| #if USE(JSVALUE64) |
| if (constant.isUnlinked()) |
| return CCallHelpers::branchPtr(cond, left, constant.unlinkedAddress()); |
| #endif |
| return CCallHelpers::branchPtr(cond, left, CCallHelpers::TrustedImmPtr(constant.pointer())); |
| } |
| |
| std::tuple<CompileTimeStructureStubInfo, LinkableConstant> addStructureStubInfo(); |
| LinkerIR::Constant addToConstantPool(LinkerIR::Type, void*); |
| |
| private: |
| friend class OSRExitJumpPlaceholder; |
| |
| // Internal implementation to compile. |
| void compileEntry(); |
| void compileSetupRegistersForEntry(); |
| void compileEntryExecutionFlag(); |
| void compileBody(); |
| void link(LinkBuffer&); |
| |
| void exitSpeculativeWithOSR(const OSRExit&, SpeculationRecovery*); |
| void linkOSRExits(); |
| void disassemble(LinkBuffer&); |
| |
| void appendExceptionHandlingOSRExit(ExitKind, unsigned eventStreamIndex, CodeOrigin, HandlerInfo* exceptionHandler, CallSiteIndex, MacroAssembler::JumpList jumpsToFail = MacroAssembler::JumpList()); |
| |
| void makeCatchOSREntryBuffer(); |
| |
| // The dataflow graph currently being generated. |
| Graph& m_graph; |
| |
| std::unique_ptr<Disassembler> m_disassembler; |
| |
| RefPtr<JITCode> m_jitCode; |
| |
| // Vector of calls out from JIT code, including exception handler information. |
| // Count of the number of CallRecords with exception handlers. |
| Vector<CallLinkRecord> m_calls; |
| JumpList m_exceptionChecks; |
| JumpList m_exceptionChecksWithCallFrameRollback; |
| |
| Vector<Label> m_blockHeads; |
| |
| |
| struct JSCallRecord { |
| JSCallRecord(Label slowPathStart, Label doneLocation, OptimizingCallLinkInfo* info) |
| : slowPathStart(slowPathStart) |
| , doneLocation(doneLocation) |
| , info(info) |
| { |
| } |
| |
| Label slowPathStart; |
| Label doneLocation; |
| OptimizingCallLinkInfo* info; |
| }; |
| |
| struct JSDirectCallRecord { |
| JSDirectCallRecord(Label slowPath, OptimizingCallLinkInfo* info) |
| : slowPath(slowPath) |
| , info(info) |
| { |
| } |
| |
| Label slowPath; |
| OptimizingCallLinkInfo* info; |
| }; |
| |
| Vector<InlineCacheWrapper<JITGetByIdGenerator>, 4> m_getByIds; |
| Vector<InlineCacheWrapper<JITGetByIdWithThisGenerator>, 4> m_getByIdsWithThis; |
| Vector<InlineCacheWrapper<JITGetByValGenerator>, 4> m_getByVals; |
| Vector<InlineCacheWrapper<JITPutByIdGenerator>, 4> m_putByIds; |
| Vector<InlineCacheWrapper<JITPutByValGenerator>, 4> m_putByVals; |
| Vector<InlineCacheWrapper<JITDelByIdGenerator>, 4> m_delByIds; |
| Vector<InlineCacheWrapper<JITDelByValGenerator>, 4> m_delByVals; |
| Vector<InlineCacheWrapper<JITInByIdGenerator>, 4> m_inByIds; |
| Vector<InlineCacheWrapper<JITInByValGenerator>, 4> m_inByVals; |
| Vector<InlineCacheWrapper<JITInstanceOfGenerator>, 4> m_instanceOfs; |
| Vector<InlineCacheWrapper<JITPrivateBrandAccessGenerator>, 4> m_privateBrandAccesses; |
| Vector<JSCallRecord, 4> m_jsCalls; |
| Vector<JSDirectCallRecord, 4> m_jsDirectCalls; |
| SegmentedVector<OSRExitCompilationInfo, 4> m_exitCompilationInfo; |
| Vector<Vector<Label>> m_exitSiteLabels; |
| Vector<DFG::OSREntryData> m_osrEntry; |
| Vector<DFG::OSRExit> m_osrExit; |
| Vector<DFG::SpeculationRecovery> m_speculationRecovery; |
| Vector<LinkerIR::Value> m_constantPool; |
| HashMap<LinkerIR::Value, LinkerIR::Constant, LinkerIR::ValueHash, LinkerIR::ValueTraits> m_constantPoolMap; |
| SegmentedVector<DFG::UnlinkedStructureStubInfo> m_unlinkedStubInfos; |
| |
| struct ExceptionHandlingOSRExitInfo { |
| OSRExitCompilationInfo& exitInfo; |
| HandlerInfo baselineExceptionHandler; |
| CallSiteIndex callSiteIndex; |
| }; |
| Vector<ExceptionHandlingOSRExitInfo> m_exceptionHandlerOSRExitCallSites; |
| |
| std::unique_ptr<SpeculativeJIT> m_speculative; |
| PCToCodeOriginMapBuilder m_pcToCodeOriginMapBuilder; |
| }; |
| |
| } } // namespace JSC::DFG |
| |
| #endif |