diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-04-10 09:28:39 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-04-10 09:28:39 +0000 |
commit | 32761a6cee1d0dee366b885b7b9c777e67885688 (patch) | |
tree | d6bec92bebfb216f4126356e55518842c2f476a1 /Source/JavaScriptCore/ftl/FTLCompile.cpp | |
parent | a4e969f4965059196ca948db781e52f7cfebf19e (diff) | |
download | WebKitGtk-tarball-32761a6cee1d0dee366b885b7b9c777e67885688.tar.gz |
webkitgtk-2.4.11webkitgtk-2.4.11
Diffstat (limited to 'Source/JavaScriptCore/ftl/FTLCompile.cpp')
-rw-r--r-- | Source/JavaScriptCore/ftl/FTLCompile.cpp | 454 |
1 files changed, 370 insertions, 84 deletions
diff --git a/Source/JavaScriptCore/ftl/FTLCompile.cpp b/Source/JavaScriptCore/ftl/FTLCompile.cpp index 2efbf8df4..6c01f4fa5 100644 --- a/Source/JavaScriptCore/ftl/FTLCompile.cpp +++ b/Source/JavaScriptCore/ftl/FTLCompile.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2016 Apple Inc. All rights reserved. + * Copyright (C) 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,123 +28,409 @@ #if ENABLE(FTL_JIT) -#include "AirCode.h" -#include "B3Generate.h" -#include "B3ProcedureInlines.h" -#include "B3StackSlot.h" #include "CodeBlockWithJITType.h" #include "CCallHelpers.h" #include "DFGCommon.h" -#include "DFGGraphSafepoint.h" -#include "DFGOperations.h" #include "DataView.h" #include "Disassembler.h" +#include "FTLExitThunkGenerator.h" +#include "FTLInlineCacheSize.h" #include "FTLJITCode.h" #include "FTLThunks.h" -#include "JITSubGenerator.h" +#include "JITStubs.h" +#include "LLVMAPI.h" #include "LinkBuffer.h" -#include "PCToCodeOriginMap.h" -#include "ScratchRegisterAllocator.h" +#include "RepatchBuffer.h" namespace JSC { namespace FTL { using namespace DFG; -void compile(State& state, Safepoint::Result& safepointResult) +static uint8_t* mmAllocateCodeSection( + void* opaqueState, uintptr_t size, unsigned alignment, unsigned, const char* sectionName) { - Graph& graph = state.graph; - CodeBlock* codeBlock = graph.m_codeBlock; - VM& vm = graph.m_vm; + + State& state = *static_cast<State*>(opaqueState); + + RELEASE_ASSERT(alignment <= jitAllocationGranule); + + RefPtr<ExecutableMemoryHandle> result = + state.graph.m_vm.executableAllocator.allocate( + state.graph.m_vm, size, state.graph.m_codeBlock, JITCompilationMustSucceed); + + state.jitCode->addHandle(result); + state.codeSectionNames.append(sectionName); + + return static_cast<uint8_t*>(result->start()); +} + +static uint8_t* mmAllocateDataSection( + void* opaqueState, uintptr_t size, unsigned alignment, unsigned sectionID, + const char* sectionName, LLVMBool isReadOnly) +{ + UNUSED_PARAM(sectionID); + UNUSED_PARAM(isReadOnly); + + State& state = *static_cast<State*>(opaqueState); + + RELEASE_ASSERT(alignment <= sizeof(LSectionWord)); + + RefCountedArray<LSectionWord> section( + (size + sizeof(LSectionWord) - 1) / sizeof(LSectionWord)); + + if (!strcmp(sectionName, "__llvm_stackmaps")) + state.stackmapsSection = section; + else { + state.jitCode->addDataSection(section); + state.dataSectionNames.append(sectionName); + } + + return bitwise_cast<uint8_t*>(section.data()); +} - { - GraphSafepoint safepoint(state.graph, safepointResult); +static LLVMBool mmApplyPermissions(void*, char**) +{ + return false; +} + +static void mmDestroy(void*) +{ +} - B3::prepareForGeneration(*state.proc); +static void dumpDataSection(RefCountedArray<LSectionWord> section, const char* prefix) +{ + for (unsigned j = 0; j < section.size(); ++j) { + char buf[32]; + snprintf(buf, sizeof(buf), "0x%lx", static_cast<unsigned long>(bitwise_cast<uintptr_t>(section.data() + j))); + dataLogF("%s%16s: 0x%016llx\n", prefix, buf, static_cast<long long>(section[j])); } +} - if (safepointResult.didGetCancelled()) +template<typename DescriptorType> +void generateICFastPath( + State& state, CodeBlock* codeBlock, GeneratedFunction generatedFunction, + StackMaps::RecordMap& recordMap, DescriptorType& ic, size_t sizeOfIC) +{ + VM& vm = state.graph.m_vm; + + StackMaps::RecordMap::iterator iter = recordMap.find(ic.stackmapID()); + if (iter == recordMap.end()) { + // It was optimized out. return; - RELEASE_ASSERT(!state.graph.m_vm.heap.isCollecting()); + } - if (state.allocationFailed) - return; + StackMaps::Record& record = iter->value; + + CCallHelpers fastPathJIT(&vm, codeBlock); + ic.m_generator.generateFastPath(fastPathJIT); + + char* startOfIC = + bitwise_cast<char*>(generatedFunction) + record.instructionOffset; + + LinkBuffer linkBuffer(vm, &fastPathJIT, startOfIC, sizeOfIC); + // Note: we could handle the !isValid() case. We just don't appear to have a + // reason to do so, yet. + RELEASE_ASSERT(linkBuffer.isValid()); - std::unique_ptr<RegisterAtOffsetList> registerOffsets = - std::make_unique<RegisterAtOffsetList>(state.proc->calleeSaveRegisters()); - if (shouldDumpDisassembly()) { - dataLog("Unwind info for ", CodeBlockWithJITType(state.graph.m_codeBlock, JITCode::FTLJIT), ":\n"); - dataLog(" ", *registerOffsets, "\n"); + MacroAssembler::AssemblerType_T::fillNops( + startOfIC + linkBuffer.size(), sizeOfIC - linkBuffer.size()); + + state.finalizer->sideCodeLinkBuffer->link( + ic.m_slowPathDone, CodeLocationLabel(startOfIC + sizeOfIC)); + + linkBuffer.link( + ic.m_generator.slowPathJump(), + state.finalizer->sideCodeLinkBuffer->locationOf(ic.m_generator.slowPathBegin())); + + ic.m_generator.finalize(linkBuffer, *state.finalizer->sideCodeLinkBuffer); +} + +static void fixFunctionBasedOnStackMaps( + State& state, CodeBlock* codeBlock, JITCode* jitCode, GeneratedFunction generatedFunction, + StackMaps::RecordMap& recordMap) +{ + VM& vm = state.graph.m_vm; + StackMaps stackmaps = jitCode->stackmaps; + + ExitThunkGenerator exitThunkGenerator(state); + exitThunkGenerator.emitThunks(); + if (exitThunkGenerator.didThings()) { + OwnPtr<LinkBuffer> linkBuffer = adoptPtr(new LinkBuffer( + vm, &exitThunkGenerator, codeBlock, JITCompilationMustSucceed)); + + ASSERT(state.finalizer->osrExit.size() == state.jitCode->osrExit.size()); + + for (unsigned i = 0; i < state.jitCode->osrExit.size(); ++i) { + OSRExitCompilationInfo& info = state.finalizer->osrExit[i]; + OSRExit& exit = jitCode->osrExit[i]; + + if (Options::verboseCompilation()) + dataLog("Handling OSR stackmap #", exit.m_stackmapID, " for ", exit.m_codeOrigin, "\n"); + + StackMaps::RecordMap::iterator iter = recordMap.find(exit.m_stackmapID); + if (iter == recordMap.end()) { + // It was optimized out. + continue; + } + + info.m_thunkAddress = linkBuffer->locationOf(info.m_thunkLabel); + + exit.m_patchableCodeOffset = linkBuffer->offsetOf(info.m_thunkJump); + } + + state.finalizer->exitThunksLinkBuffer = linkBuffer.release(); } - state.graph.m_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"); + + if (!state.getByIds.isEmpty() || !state.putByIds.isEmpty()) { + CCallHelpers slowPathJIT(&vm, codeBlock); + + for (unsigned i = state.getByIds.size(); i--;) { + GetByIdDescriptor& getById = state.getByIds[i]; + + if (Options::verboseCompilation()) + dataLog("Handling GetById stackmap #", getById.stackmapID(), "\n"); + + StackMaps::RecordMap::iterator iter = recordMap.find(getById.stackmapID()); + if (iter == recordMap.end()) { + // It was optimized out. + continue; + } + + StackMaps::Record& record = iter->value; + + // FIXME: LLVM should tell us which registers are live. + RegisterSet usedRegisters = RegisterSet::allRegisters(); + + GPRReg result = record.locations[0].directGPR(); + GPRReg callFrameRegister = record.locations[1].directGPR(); + GPRReg base = record.locations[2].directGPR(); + + JITGetByIdGenerator gen( + codeBlock, getById.codeOrigin(), usedRegisters, callFrameRegister, + JSValueRegs(base), JSValueRegs(result), false); + + MacroAssembler::Label begin = slowPathJIT.label(); + + MacroAssembler::Call call = callOperation( + state, usedRegisters, slowPathJIT, operationGetByIdOptimize, result, + callFrameRegister, gen.stubInfo(), base, getById.uid()); + + gen.reportSlowPathCall(begin, call); + + getById.m_slowPathDone = slowPathJIT.jump(); + getById.m_generator = gen; + } + + for (unsigned i = state.putByIds.size(); i--;) { + PutByIdDescriptor& putById = state.putByIds[i]; + + if (Options::verboseCompilation()) + dataLog("Handling PutById stackmap #", putById.stackmapID(), "\n"); + + StackMaps::RecordMap::iterator iter = recordMap.find(putById.stackmapID()); + if (iter == recordMap.end()) { + // It was optimized out. + continue; + } + + StackMaps::Record& record = iter->value; + + // FIXME: LLVM should tell us which registers are live. + RegisterSet usedRegisters = RegisterSet::allRegisters(); + + GPRReg callFrameRegister = record.locations[0].directGPR(); + GPRReg base = record.locations[1].directGPR(); + GPRReg value = record.locations[2].directGPR(); + + JITPutByIdGenerator gen( + codeBlock, putById.codeOrigin(), usedRegisters, callFrameRegister, + JSValueRegs(base), JSValueRegs(value), MacroAssembler::scratchRegister, + false, putById.ecmaMode(), putById.putKind()); + + MacroAssembler::Label begin = slowPathJIT.label(); + + MacroAssembler::Call call = callOperation( + state, usedRegisters, slowPathJIT, gen.slowPathFunction(), callFrameRegister, + gen.stubInfo(), value, base, putById.uid()); + + gen.reportSlowPathCall(begin, call); + + putById.m_slowPathDone = slowPathJIT.jump(); + putById.m_generator = gen; + } + + state.finalizer->sideCodeLinkBuffer = adoptPtr( + new LinkBuffer(vm, &slowPathJIT, codeBlock, JITCompilationMustSucceed)); + + for (unsigned i = state.getByIds.size(); i--;) { + generateICFastPath( + state, codeBlock, generatedFunction, recordMap, state.getByIds[i], + sizeOfGetById()); + } + for (unsigned i = state.putByIds.size(); i--;) { + generateICFastPath( + state, codeBlock, generatedFunction, recordMap, state.putByIds[i], + sizeOfPutById()); + } } - for (unsigned i = graph.m_inlineVariableData.size(); i--;) { - InlineCallFrame* inlineCallFrame = graph.m_inlineVariableData[i].inlineCallFrame; + RepatchBuffer repatchBuffer(codeBlock); + + for (unsigned exitIndex = jitCode->osrExit.size(); exitIndex--;) { + OSRExitCompilationInfo& info = state.finalizer->osrExit[exitIndex]; + OSRExit& exit = jitCode->osrExit[exitIndex]; + StackMaps::RecordMap::iterator iter = recordMap.find(exit.m_stackmapID); + if (iter == recordMap.end()) { + // This could happen if LLVM optimizes out an OSR exit. + continue; + } - if (inlineCallFrame->argumentCountRegister.isValid()) - inlineCallFrame->argumentCountRegister += localsOffset; + StackMaps::Record& record = iter->value; - for (unsigned argument = inlineCallFrame->arguments.size(); argument-- > 1;) { - inlineCallFrame->arguments[argument] = - inlineCallFrame->arguments[argument].withLocalsOffset(localsOffset); - } + CodeLocationLabel source = CodeLocationLabel( + bitwise_cast<char*>(generatedFunction) + record.instructionOffset); - if (inlineCallFrame->isClosureCall) { - inlineCallFrame->calleeRecovery = - inlineCallFrame->calleeRecovery.withLocalsOffset(localsOffset); + if (info.m_isInvalidationPoint) { + jitCode->common.jumpReplacements.append(JumpReplacement(source, info.m_thunkAddress)); + continue; } - - if (graph.hasDebuggerEnabled()) - codeBlock->setScopeRegister(codeBlock->scopeRegister() + localsOffset); + + repatchBuffer.replaceWithJump(source, info.m_thunkAddress); } - 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); +} + +void compile(State& state) +{ + char* error = 0; + + LLVMMCJITCompilerOptions options; + llvm->InitializeMCJITCompilerOptions(&options, sizeof(options)); + options.OptLevel = Options::llvmBackendOptimizationLevel(); + options.NoFramePointerElim = true; + if (Options::useLLVMSmallCodeModel()) + options.CodeModel = LLVMCodeModelSmall; + options.EnableFastISel = Options::enableLLVMFastISel(); + options.MCJMM = llvm->CreateSimpleMCJITMemoryManager( + &state, mmAllocateCodeSection, mmAllocateDataSection, mmApplyPermissions, mmDestroy); + + LLVMExecutionEngineRef engine; + + if (llvm->CreateMCJITCompilerForModule(&engine, state.module, &options, sizeof(options), &error)) { + dataLog("FATAL: Could not create LLVM execution engine: ", error, "\n"); + CRASH(); } - // We will add exception handlers while generating. - codeBlock->clearExceptionHandlers(); - - CCallHelpers jit(&vm, codeBlock); - B3::generate(*state.proc, jit); - - // Emit the exception handler. - *state.exceptionHandler = jit.label(); - jit.copyCalleeSavesToVMCalleeSavesBuffer(); - jit.move(MacroAssembler::TrustedImmPtr(jit.vm()), GPRInfo::argumentGPR0); - jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR1); - CCallHelpers::Call call = jit.call(); - jit.jumpToExceptionHandler(); - jit.addLinkTask( - [=] (LinkBuffer& linkBuffer) { - linkBuffer.link(call, FunctionPtr(lookupExceptionHandler)); - }); - - state.finalizer->b3CodeLinkBuffer = std::make_unique<LinkBuffer>( - vm, jit, codeBlock, JITCompilationCanFail); - if (state.finalizer->b3CodeLinkBuffer->didFailToAllocate()) { - state.allocationFailed = true; - return; + LLVMPassManagerRef functionPasses = 0; + LLVMPassManagerRef modulePasses; + + if (Options::llvmSimpleOpt()) { + modulePasses = llvm->CreatePassManager(); + llvm->AddTargetData(llvm->GetExecutionEngineTargetData(engine), modulePasses); + llvm->AddPromoteMemoryToRegisterPass(modulePasses); + llvm->AddConstantPropagationPass(modulePasses); + llvm->AddInstructionCombiningPass(modulePasses); + llvm->AddBasicAliasAnalysisPass(modulePasses); + llvm->AddTypeBasedAliasAnalysisPass(modulePasses); + llvm->AddGVNPass(modulePasses); + llvm->AddCFGSimplificationPass(modulePasses); + llvm->RunPassManager(modulePasses, state.module); + } else { + LLVMPassManagerBuilderRef passBuilder = llvm->PassManagerBuilderCreate(); + llvm->PassManagerBuilderSetOptLevel(passBuilder, Options::llvmOptimizationLevel()); + llvm->PassManagerBuilderSetSizeLevel(passBuilder, Options::llvmSizeLevel()); + + functionPasses = llvm->CreateFunctionPassManagerForModule(state.module); + modulePasses = llvm->CreatePassManager(); + + llvm->AddTargetData(llvm->GetExecutionEngineTargetData(engine), modulePasses); + + llvm->PassManagerBuilderPopulateFunctionPassManager(passBuilder, functionPasses); + llvm->PassManagerBuilderPopulateModulePassManager(passBuilder, modulePasses); + + llvm->PassManagerBuilderDispose(passBuilder); + + llvm->InitializeFunctionPassManager(functionPasses); + for (LValue function = llvm->GetFirstFunction(state.module); function; function = llvm->GetNextFunction(function)) + llvm->RunFunctionPassManager(functionPasses, function); + llvm->FinalizeFunctionPassManager(functionPasses); + + llvm->RunPassManager(modulePasses, state.module); } + + if (DFG::shouldShowDisassembly() || DFG::verboseCompilationEnabled()) + state.dumpState("after optimization"); - B3::PCToOriginMap originMap = state.proc->releasePCToOriginMap(); - if (vm.shouldBuilderPCToCodeOriginMapping()) - codeBlock->setPCToCodeOriginMap(std::make_unique<PCToCodeOriginMap>(PCToCodeOriginMapBuilder(vm, WTFMove(originMap)), *state.finalizer->b3CodeLinkBuffer)); + // FIXME: Need to add support for the case where JIT memory allocation failed. + // https://bugs.webkit.org/show_bug.cgi?id=113620 + state.generatedFunction = reinterpret_cast<GeneratedFunction>(llvm->GetPointerToGlobal(engine, state.function)); + if (functionPasses) + llvm->DisposePassManager(functionPasses); + llvm->DisposePassManager(modulePasses); + llvm->DisposeExecutionEngine(engine); - state.generatedFunction = bitwise_cast<GeneratedFunction>( - state.finalizer->b3CodeLinkBuffer->entrypoint().executableAddress()); - state.jitCode->initializeB3Byproducts(state.proc->releaseByproducts()); + if (shouldShowDisassembly()) { + for (unsigned i = 0; i < state.jitCode->handles().size(); ++i) { + ExecutableMemoryHandle* handle = state.jitCode->handles()[i].get(); + dataLog( + "Generated LLVM code for ", + CodeBlockWithJITType(state.graph.m_codeBlock, JITCode::DFGJIT), + " #", i, ", ", state.codeSectionNames[i], ":\n"); + disassemble( + MacroAssemblerCodePtr(handle->start()), handle->sizeInBytes(), + " ", WTF::dataFile(), LLVMSubset); + } + + for (unsigned i = 0; i < state.jitCode->dataSections().size(); ++i) { + const RefCountedArray<LSectionWord>& section = state.jitCode->dataSections()[i]; + dataLog( + "Generated LLVM data section for ", + CodeBlockWithJITType(state.graph.m_codeBlock, JITCode::DFGJIT), + " #", i, ", ", state.dataSectionNames[i], ":\n"); + dumpDataSection(section, " "); + } + } + + if (state.stackmapsSection.size()) { + if (shouldShowDisassembly()) { + dataLog( + "Generated LLVM stackmaps section for ", + CodeBlockWithJITType(state.graph.m_codeBlock, JITCode::DFGJIT), ":\n"); + dataLog(" Raw data:\n"); + dumpDataSection(state.stackmapsSection, " "); + } + + RefPtr<DataView> stackmapsData = DataView::create( + ArrayBuffer::create(state.stackmapsSection.data(), state.stackmapsSection.byteSize())); + state.jitCode->stackmaps.parse(stackmapsData.get()); + + if (shouldShowDisassembly()) { + dataLog(" Structured data:\n"); + state.jitCode->stackmaps.dumpMultiline(WTF::dataFile(), " "); + } + + StackMaps::RecordMap recordMap = state.jitCode->stackmaps.getRecordMap(); + fixFunctionBasedOnStackMaps( + state, state.graph.m_codeBlock, state.jitCode.get(), state.generatedFunction, + recordMap); + + if (shouldShowDisassembly()) { + for (unsigned i = 0; i < state.jitCode->handles().size(); ++i) { + if (state.codeSectionNames[i] != "__text") + continue; + + ExecutableMemoryHandle* handle = state.jitCode->handles()[i].get(); + dataLog( + "Generated LLVM code after stackmap-based fix-up for ", + CodeBlockWithJITType(state.graph.m_codeBlock, JITCode::DFGJIT), + " #", i, ", ", state.codeSectionNames[i], ":\n"); + disassemble( + MacroAssemblerCodePtr(handle->start()), handle->sizeInBytes(), + " ", WTF::dataFile(), LLVMSubset); + } + } + } + + state.module = 0; // We no longer own the module. } } } // namespace JSC::FTL |