/* * Copyright (C) 2015-2018 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. */ #include "config.h" #include "FTLCompile.h" #if ENABLE(FTL_JIT) #include "AirCode.h" #include "AirDisassembler.h" #include "B3Generate.h" #include "B3ProcedureInlines.h" #include "B3StackSlot.h" #include "B3Value.h" #include "B3ValueInlines.h" #include "CodeBlockWithJITType.h" #include "CCallHelpers.h" #include "DFGCommon.h" #include "DFGGraphSafepoint.h" #include "DFGOperations.h" #include "DataView.h" #include "Disassembler.h" #include "FTLJITCode.h" #include "FTLThunks.h" #include "JITSubGenerator.h" #include "JSCInlines.h" #include "LinkBuffer.h" #include "PCToCodeOriginMap.h" #include "ScratchRegisterAllocator.h" #include namespace JSC { namespace FTL { using namespace DFG; void compile(State& state, Safepoint::Result& safepointResult) { Graph& graph = state.graph; CodeBlock* codeBlock = graph.m_codeBlock; VM& vm = graph.m_vm; if (shouldDumpDisassembly()) state.proc->code().setDisassembler(std::make_unique()); { GraphSafepoint safepoint(state.graph, safepointResult); B3::prepareForGeneration(*state.proc); } if (safepointResult.didGetCancelled()) return; RELEASE_ASSERT(!state.graph.m_vm.heap.worldIsStopped()); if (state.allocationFailed) return; std::unique_ptr registerOffsets = std::make_unique(state.proc->calleeSaveRegisterAtOffsetList()); if (shouldDumpDisassembly()) dataLog("Unwind info for ", CodeBlockWithJITType(codeBlock, JITType::FTLJIT), ": ", *registerOffsets, "\n"); codeBlock->setCalleeSaveRegisters(WTFMove(registerOffsets)); ASSERT(!(state.proc->frameSize() % sizeof(EncodedJSValue))); state.jitCode->common.frameRegisterCount = state.proc->frameSize() / sizeof(EncodedJSValue); int localsOffset = state.capturedValue->offsetFromFP() / sizeof(EncodedJSValue) + graph.m_nextMachineLocal; if (shouldDumpDisassembly()) { dataLog( "localsOffset = ", localsOffset, " for stack slot: ", pointerDump(state.capturedValue), " at ", RawPointer(state.capturedValue), "\n"); } for (unsigned i = graph.m_inlineVariableData.size(); i--;) { InlineCallFrame* inlineCallFrame = graph.m_inlineVariableData[i].inlineCallFrame; if (inlineCallFrame->argumentCountRegister.isValid()) inlineCallFrame->argumentCountRegister += localsOffset; for (unsigned argument = inlineCallFrame->argumentsWithFixup.size(); argument-- > 1;) { inlineCallFrame->argumentsWithFixup[argument] = inlineCallFrame->argumentsWithFixup[argument].withLocalsOffset(localsOffset); } if (inlineCallFrame->isClosureCall) { inlineCallFrame->calleeRecovery = inlineCallFrame->calleeRecovery.withLocalsOffset(localsOffset); } } // Note that the scope register could be invalid here if the original code had CallEval but it // got killed. That's because it takes the CallEval to cause the scope register to be kept alive // unless the debugger is also enabled. if (graph.needsScopeRegister() && codeBlock->scopeRegister().isValid()) codeBlock->setScopeRegister(codeBlock->scopeRegister() + localsOffset); for (OSRExitDescriptor& descriptor : state.jitCode->osrExitDescriptors) { for (unsigned i = descriptor.m_values.size(); i--;) descriptor.m_values[i] = descriptor.m_values[i].withLocalsOffset(localsOffset); for (ExitTimeObjectMaterialization* materialization : descriptor.m_materializations) materialization->accountForLocalsOffset(localsOffset); } // We will add exception handlers while generating. codeBlock->clearExceptionHandlers(); CCallHelpers jit(codeBlock); B3::generate(*state.proc, jit); // Emit the exception handler. *state.exceptionHandler = jit.label(); jit.copyCalleeSavesToEntryFrameCalleeSavesBuffer(vm.topEntryFrame); jit.move(MacroAssembler::TrustedImmPtr(&vm), GPRInfo::argumentGPR0); jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR1); CCallHelpers::Call call = jit.call(OperationPtrTag); jit.jumpToExceptionHandler(vm); jit.addLinkTask( [=] (LinkBuffer& linkBuffer) { linkBuffer.link(call, FunctionPtr(lookupExceptionHandler)); }); state.finalizer->b3CodeLinkBuffer = std::make_unique(jit, codeBlock, JITCompilationCanFail); if (state.finalizer->b3CodeLinkBuffer->didFailToAllocate()) { state.allocationFailed = true; return; } B3::PCToOriginMap originMap = state.proc->releasePCToOriginMap(); if (vm.shouldBuilderPCToCodeOriginMapping()) codeBlock->setPCToCodeOriginMap(std::make_unique(PCToCodeOriginMapBuilder(vm, WTFMove(originMap)), *state.finalizer->b3CodeLinkBuffer)); CodeLocationLabel label = state.finalizer->b3CodeLinkBuffer->locationOf(state.proc->entrypointLabel(0)); state.generatedFunction = label; state.jitCode->initializeB3Byproducts(state.proc->releaseByproducts()); for (auto pair : state.graph.m_entrypointIndexToCatchBytecodeOffset) { unsigned catchBytecodeOffset = pair.value; unsigned entrypointIndex = pair.key; Vector argumentFormats = state.graph.m_argumentFormats[entrypointIndex]; state.jitCode->common.appendCatchEntrypoint( catchBytecodeOffset, state.finalizer->b3CodeLinkBuffer->locationOf(state.proc->entrypointLabel(entrypointIndex)), WTFMove(argumentFormats)); } state.jitCode->common.finalizeCatchEntrypoints(); if (B3::Air::Disassembler* disassembler = state.proc->code().disassembler()) { PrintStream& out = WTF::dataFile(); out.print("Generated ", state.graph.m_plan.mode(), " code for ", CodeBlockWithJITType(state.graph.m_codeBlock, JITType::FTLJIT), ", instructions size = ", state.graph.m_codeBlock->instructionsSize(), ":\n"); LinkBuffer& linkBuffer = *state.finalizer->b3CodeLinkBuffer; B3::Value* currentB3Value = nullptr; Node* currentDFGNode = nullptr; HashSet printedValues; HashSet printedNodes; const char* dfgPrefix = " "; const char* b3Prefix = " "; const char* airPrefix = " "; const char* asmPrefix = " "; auto printDFGNode = [&] (Node* node) { if (currentDFGNode == node) return; currentDFGNode = node; if (!currentDFGNode) return; HashSet localPrintedNodes; WTF::Function printNodeRecursive = [&] (Node* node) { if (printedNodes.contains(node) || localPrintedNodes.contains(node)) return; localPrintedNodes.add(node); graph.doToChildren(node, [&] (Edge child) { printNodeRecursive(child.node()); }); graph.dump(out, dfgPrefix, node); }; printNodeRecursive(node); printedNodes.add(node); }; auto printB3Value = [&] (B3::Value* value) { if (currentB3Value == value) return; currentB3Value = value; if (!currentB3Value) return; printDFGNode(bitwise_cast(value->origin().data())); HashSet localPrintedValues; auto printValueRecursive = recursableLambda([&] (auto self, B3::Value* value) -> void { if (printedValues.contains(value) || localPrintedValues.contains(value)) return; localPrintedValues.add(value); for (unsigned i = 0; i < value->numChildren(); i++) self(value->child(i)); out.print(b3Prefix); value->deepDump(state.proc.get(), out); out.print("\n"); }); printValueRecursive(currentB3Value); printedValues.add(value); }; auto forEachInst = scopedLambda([&] (B3::Air::Inst& inst) { printB3Value(inst.origin); }); disassembler->dump(state.proc->code(), out, linkBuffer, airPrefix, asmPrefix, forEachInst); linkBuffer.didAlreadyDisassemble(); } } } } // namespace JSC::FTL #endif // ENABLE(FTL_JIT)