diff options
| author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2015-05-20 09:56:07 +0000 |
|---|---|---|
| committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2015-05-20 09:56:07 +0000 |
| commit | 41386e9cb918eed93b3f13648cbef387e371e451 (patch) | |
| tree | a97f9d7bd1d9d091833286085f72da9d83fd0606 /Source/JavaScriptCore/ftl/FTLCompile.cpp | |
| parent | e15dd966d523731101f70ccf768bba12435a0208 (diff) | |
| download | WebKitGtk-tarball-41386e9cb918eed93b3f13648cbef387e371e451.tar.gz | |
webkitgtk-2.4.9webkitgtk-2.4.9
Diffstat (limited to 'Source/JavaScriptCore/ftl/FTLCompile.cpp')
| -rw-r--r-- | Source/JavaScriptCore/ftl/FTLCompile.cpp | 819 |
1 files changed, 187 insertions, 632 deletions
diff --git a/Source/JavaScriptCore/ftl/FTLCompile.cpp b/Source/JavaScriptCore/ftl/FTLCompile.cpp index c6fcca05e..6c01f4fa5 100644 --- a/Source/JavaScriptCore/ftl/FTLCompile.cpp +++ b/Source/JavaScriptCore/ftl/FTLCompile.cpp @@ -1,7 +1,5 @@ /* - * Copyright (C) 2013-2015 Apple Inc. All rights reserved. - * Copyright (C) 2014 Samsung Electronics - * Copyright (C) 2014 University of Szeged + * 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 @@ -33,14 +31,12 @@ #include "CodeBlockWithJITType.h" #include "CCallHelpers.h" #include "DFGCommon.h" -#include "DFGGraphSafepoint.h" #include "DataView.h" #include "Disassembler.h" #include "FTLExitThunkGenerator.h" #include "FTLInlineCacheSize.h" #include "FTLJITCode.h" #include "FTLThunks.h" -#include "FTLUnwindInfo.h" #include "JITStubs.h" #include "LLVMAPI.h" #include "LinkBuffer.h" @@ -53,30 +49,14 @@ using namespace DFG; static uint8_t* mmAllocateCodeSection( void* opaqueState, uintptr_t size, unsigned alignment, unsigned, const char* sectionName) { + 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, JITCompilationCanFail); - - if (!result) { - // Signal failure. This compilation will get tossed. - state.allocationFailed = true; - - // Fake an allocation, since LLVM cannot handle failures in the memory manager. - RefPtr<DataSection> fakeSection = adoptRef(new DataSection(size, jitAllocationGranule)); - state.jitCode->addDataSection(fakeSection); - return bitwise_cast<uint8_t*>(fakeSection->base()); - } - - // LLVM used to put __compact_unwind in a code section. We keep this here defensively, - // for clients that use older LLVMs. - if (!strcmp(sectionName, SECTION_NAME("compact_unwind"))) { - state.unwindDataSection = result->start(); - state.unwindDataSectionSize = result->sizeInBytes(); - } + state.graph.m_vm, size, state.graph.m_codeBlock, JITCompilationMustSucceed); state.jitCode->addHandle(result); state.codeSectionNames.append(sectionName); @@ -91,32 +71,21 @@ static uint8_t* mmAllocateDataSection( UNUSED_PARAM(sectionID); UNUSED_PARAM(isReadOnly); - // Allocate the GOT in the code section to make it reachable for all code. - if (!strcmp(sectionName, SECTION_NAME("got"))) - return mmAllocateCodeSection(opaqueState, size, alignment, sectionID, sectionName); - State& state = *static_cast<State*>(opaqueState); - - RefPtr<DataSection> section = adoptRef(new DataSection(size, alignment)); - - if (!strcmp(sectionName, SECTION_NAME("llvm_stackmaps"))) + + 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); -#if OS(DARWIN) - if (!strcmp(sectionName, SECTION_NAME("compact_unwind"))) { -#elif OS(LINUX) - if (!strcmp(sectionName, SECTION_NAME("eh_frame"))) { -#else -#error "Unrecognized OS" -#endif - state.unwindDataSection = section->base(); - state.unwindDataSectionSize = size; - } } - - return bitwise_cast<uint8_t*>(section->base()); + + return bitwise_cast<uint8_t*>(section.data()); } static LLVMBool mmApplyPermissions(void*, char**) @@ -128,74 +97,13 @@ static void mmDestroy(void*) { } -static void dumpDataSection(DataSection* section, const char* prefix) +static void dumpDataSection(RefCountedArray<LSectionWord> section, const char* prefix) { - for (unsigned j = 0; j < section->size() / sizeof(int64_t); ++j) { + for (unsigned j = 0; j < section.size(); ++j) { char buf[32]; - int64_t* wordPointer = static_cast<int64_t*>(section->base()) + j; - snprintf(buf, sizeof(buf), "0x%lx", static_cast<unsigned long>(bitwise_cast<uintptr_t>(wordPointer))); - dataLogF("%s%16s: 0x%016llx\n", prefix, buf, static_cast<long long>(*wordPointer)); - } -} - -static int offsetOfStackRegion(StackMaps::RecordMap& recordMap, uint32_t stackmapID) -{ - if (stackmapID == UINT_MAX) - return 0; - - StackMaps::RecordMap::iterator iter = recordMap.find(stackmapID); - RELEASE_ASSERT(iter != recordMap.end()); - RELEASE_ASSERT(iter->value.size() == 1); - RELEASE_ASSERT(iter->value[0].locations.size() == 1); - Location capturedLocation = - Location::forStackmaps(nullptr, iter->value[0].locations[0]); - RELEASE_ASSERT(capturedLocation.kind() == Location::Register); - RELEASE_ASSERT(capturedLocation.gpr() == GPRInfo::callFrameRegister); - RELEASE_ASSERT(!(capturedLocation.addend() % sizeof(Register))); - return capturedLocation.addend() / sizeof(Register); -} - -static void generateInlineIfPossibleOutOfLineIfNot(State& state, VM& vm, CodeBlock* codeBlock, CCallHelpers& code, char* startOfInlineCode, size_t sizeOfInlineCode, const char* codeDescription, const std::function<void(LinkBuffer&, CCallHelpers&, bool wasCompiledInline)>& callback) -{ - std::unique_ptr<LinkBuffer> codeLinkBuffer; - size_t actualCodeSize = code.m_assembler.buffer().codeSize(); - - if (actualCodeSize <= sizeOfInlineCode) { - LinkBuffer codeLinkBuffer(vm, code, startOfInlineCode, sizeOfInlineCode); - - // Fill the remainder of the inline space with nops to avoid confusing the disassembler. - MacroAssembler::AssemblerType_T::fillNops(bitwise_cast<char*>(startOfInlineCode) + actualCodeSize, sizeOfInlineCode - actualCodeSize); - - callback(codeLinkBuffer, code, true); - - return; + 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 there isn't enough space in the provided inline code area, allocate out of line - // executable memory to link the provided code. Place a jump at the beginning of the - // inline area and jump to the out of line code. Similarly return by appending a jump - // to the provided code that goes to the instruction after the inline code. - // Fill the middle with nop's. - MacroAssembler::Jump returnToMainline = code.jump(); - - // Allocate out of line executable memory and link the provided code there. - codeLinkBuffer = std::make_unique<LinkBuffer>(vm, code, codeBlock, JITCompilationMustSucceed); - - // Plant a jmp in the inline buffer to the out of line code. - MacroAssembler callToOutOfLineCode; - MacroAssembler::Jump jumpToOutOfLine = callToOutOfLineCode.jump(); - LinkBuffer inlineBuffer(vm, callToOutOfLineCode, startOfInlineCode, sizeOfInlineCode); - inlineBuffer.link(jumpToOutOfLine, codeLinkBuffer->entrypoint()); - - // Fill the remainder of the inline space with nops to avoid confusing the disassembler. - MacroAssembler::AssemblerType_T::fillNops(bitwise_cast<char*>(startOfInlineCode) + inlineBuffer.size(), sizeOfInlineCode - inlineBuffer.size()); - - // Link the end of the out of line code to right after the inline area. - codeLinkBuffer->link(returnToMainline, CodeLocationLabel(MacroAssemblerCodePtr::createFromExecutableAddress(startOfInlineCode)).labelAtOffset(sizeOfInlineCode)); - - callback(*codeLinkBuffer.get(), code, false); - - state.finalizer->outOfLineCodeInfos.append(OutOfLineCodeInfo(WTF::move(codeLinkBuffer), codeDescription)); } template<typename DescriptorType> @@ -211,360 +119,148 @@ void generateICFastPath( return; } - Vector<StackMaps::Record>& records = iter->value; - - RELEASE_ASSERT(records.size() == ic.m_generators.size()); - - for (unsigned i = records.size(); i--;) { - StackMaps::Record& record = records[i]; - auto generator = ic.m_generators[i]; + StackMaps::Record& record = iter->value; - CCallHelpers fastPathJIT(&vm, codeBlock); - generator.generateFastPath(fastPathJIT); - - char* startOfIC = - bitwise_cast<char*>(generatedFunction) + record.instructionOffset; + CCallHelpers fastPathJIT(&vm, codeBlock); + ic.m_generator.generateFastPath(fastPathJIT); - generateInlineIfPossibleOutOfLineIfNot(state, vm, codeBlock, fastPathJIT, startOfIC, sizeOfIC, "inline cache fast path", [&] (LinkBuffer& linkBuffer, CCallHelpers&, bool) { - state.finalizer->sideCodeLinkBuffer->link(ic.m_slowPathDone[i], - CodeLocationLabel(startOfIC + sizeOfIC)); - - linkBuffer.link(generator.slowPathJump(), - state.finalizer->sideCodeLinkBuffer->locationOf(generator.slowPathBegin())); - - generator.finalize(linkBuffer, *state.finalizer->sideCodeLinkBuffer); - }); - } -} - -static void generateCheckInICFastPath( - State& state, CodeBlock* codeBlock, GeneratedFunction generatedFunction, - StackMaps::RecordMap& recordMap, CheckInDescriptor& 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; - } - - Vector<StackMaps::Record>& records = iter->value; + 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()); - RELEASE_ASSERT(records.size() == ic.m_generators.size()); - - for (unsigned i = records.size(); i--;) { - StackMaps::Record& record = records[i]; - auto generator = ic.m_generators[i]; - - StructureStubInfo& stubInfo = *generator.m_stub; - auto call = generator.m_slowCall; - auto slowPathBegin = generator.m_beginLabel; - - CCallHelpers fastPathJIT(&vm, codeBlock); - - auto jump = fastPathJIT.patchableJump(); - auto done = fastPathJIT.label(); - - char* startOfIC = - bitwise_cast<char*>(generatedFunction) + record.instructionOffset; - - auto postLink = [&] (LinkBuffer& fastPath, CCallHelpers&, bool) { - LinkBuffer& slowPath = *state.finalizer->sideCodeLinkBuffer; - - state.finalizer->sideCodeLinkBuffer->link( - ic.m_slowPathDone[i], CodeLocationLabel(startOfIC + sizeOfIC)); - - CodeLocationLabel slowPathBeginLoc = slowPath.locationOf(slowPathBegin); - fastPath.link(jump, slowPathBeginLoc); - - CodeLocationCall callReturnLocation = slowPath.locationOf(call); - - stubInfo.patch.deltaCallToDone = MacroAssembler::differenceBetweenCodePtr( - callReturnLocation, fastPath.locationOf(done)); - - stubInfo.patch.deltaCallToJump = MacroAssembler::differenceBetweenCodePtr( - callReturnLocation, fastPath.locationOf(jump)); - stubInfo.callReturnLocation = callReturnLocation; - stubInfo.patch.deltaCallToSlowCase = MacroAssembler::differenceBetweenCodePtr( - callReturnLocation, slowPathBeginLoc); - }; - - generateInlineIfPossibleOutOfLineIfNot(state, vm, codeBlock, fastPathJIT, startOfIC, sizeOfIC, "CheckIn inline cache", postLink); - } -} - - -static RegisterSet usedRegistersFor(const StackMaps::Record& record) -{ - if (Options::assumeAllRegsInFTLICAreLive()) - return RegisterSet::allRegisters(); - return RegisterSet(record.usedRegisterSet(), RegisterSet::calleeSaveRegisters()); -} - -template<typename CallType> -void adjustCallICsForStackmaps(Vector<CallType>& calls, StackMaps::RecordMap& recordMap) -{ - // Handling JS calls is weird: we need to ensure that we sort them by the PC in LLVM - // generated code. That implies first pruning the ones that LLVM didn't generate. - - Vector<CallType> oldCalls; - oldCalls.swap(calls); + MacroAssembler::AssemblerType_T::fillNops( + startOfIC + linkBuffer.size(), sizeOfIC - linkBuffer.size()); - for (unsigned i = 0; i < oldCalls.size(); ++i) { - CallType& call = oldCalls[i]; - - StackMaps::RecordMap::iterator iter = recordMap.find(call.stackmapID()); - if (iter == recordMap.end()) - continue; - - for (unsigned j = 0; j < iter->value.size(); ++j) { - CallType copy = call; - copy.m_instructionOffset = iter->value[j].instructionOffset; - calls.append(copy); - } - } - - std::sort(calls.begin(), calls.end()); + 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, bool didSeeUnwindInfo) + StackMaps::RecordMap& recordMap) { - Graph& graph = state.graph; - VM& vm = graph.m_vm; + VM& vm = state.graph.m_vm; StackMaps stackmaps = jitCode->stackmaps; - - int localsOffset = offsetOfStackRegion(recordMap, state.capturedStackmapID) + graph.m_nextMachineLocal; - int varargsSpillSlotsOffset = offsetOfStackRegion(recordMap, state.varargsSpillSlotsStackmapID); - - 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->arguments.size(); argument-- > 1;) { - inlineCallFrame->arguments[argument] = - inlineCallFrame->arguments[argument].withLocalsOffset(localsOffset); - } - - if (inlineCallFrame->isClosureCall) { - inlineCallFrame->calleeRecovery = - inlineCallFrame->calleeRecovery.withLocalsOffset(localsOffset); - } - - if (graph.hasDebuggerEnabled()) - codeBlock->setScopeRegister(codeBlock->scopeRegister() + localsOffset); - } - - MacroAssembler::Label stackOverflowException; - - { - CCallHelpers checkJIT(&vm, codeBlock); - - // At this point it's perfectly fair to just blow away all state and restore the - // JS JIT view of the universe. - checkJIT.move(MacroAssembler::TrustedImmPtr(&vm), GPRInfo::argumentGPR0); - checkJIT.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR1); - MacroAssembler::Call callLookupExceptionHandler = checkJIT.call(); - checkJIT.jumpToExceptionHandler(); - - stackOverflowException = checkJIT.label(); - checkJIT.move(MacroAssembler::TrustedImmPtr(&vm), GPRInfo::argumentGPR0); - checkJIT.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR1); - MacroAssembler::Call callLookupExceptionHandlerFromCallerFrame = checkJIT.call(); - checkJIT.jumpToExceptionHandler(); - - auto linkBuffer = std::make_unique<LinkBuffer>( - vm, checkJIT, codeBlock, JITCompilationCanFail); - if (linkBuffer->didFailToAllocate()) { - state.allocationFailed = true; - return; - } - linkBuffer->link(callLookupExceptionHandler, FunctionPtr(lookupExceptionHandler)); - linkBuffer->link(callLookupExceptionHandlerFromCallerFrame, FunctionPtr(lookupExceptionHandlerFromCallerFrame)); - - state.finalizer->handleExceptionsLinkBuffer = WTF::move(linkBuffer); - } ExitThunkGenerator exitThunkGenerator(state); exitThunkGenerator.emitThunks(); if (exitThunkGenerator.didThings()) { - RELEASE_ASSERT(state.finalizer->osrExit.size()); - RELEASE_ASSERT(didSeeUnwindInfo); + OwnPtr<LinkBuffer> linkBuffer = adoptPtr(new LinkBuffer( + vm, &exitThunkGenerator, codeBlock, JITCompilationMustSucceed)); - auto linkBuffer = std::make_unique<LinkBuffer>( - vm, exitThunkGenerator, codeBlock, JITCompilationCanFail); - if (linkBuffer->didFailToAllocate()) { - state.allocationFailed = true; - return; - } - - RELEASE_ASSERT(state.finalizer->osrExit.size() == state.jitCode->osrExit.size()); + 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 (verboseCompilationEnabled()) + if (Options::verboseCompilation()) dataLog("Handling OSR stackmap #", exit.m_stackmapID, " for ", exit.m_codeOrigin, "\n"); - - auto iter = recordMap.find(exit.m_stackmapID); + + 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); - - for (unsigned j = exit.m_values.size(); j--;) - exit.m_values[j] = exit.m_values[j].withLocalsOffset(localsOffset); - for (ExitTimeObjectMaterialization* materialization : exit.m_materializations) - materialization->accountForLocalsOffset(localsOffset); - if (verboseCompilationEnabled()) { - DumpContext context; - dataLog(" Exit values: ", inContext(exit.m_values, &context), "\n"); - if (!exit.m_materializations.isEmpty()) { - dataLog(" Materializations: \n"); - for (ExitTimeObjectMaterialization* materialization : exit.m_materializations) - dataLog(" Materialize(", pointerDump(materialization), ")\n"); - } - } + exit.m_patchableCodeOffset = linkBuffer->offsetOf(info.m_thunkJump); } - state.finalizer->exitThunksLinkBuffer = WTF::move(linkBuffer); + state.finalizer->exitThunksLinkBuffer = linkBuffer.release(); } - if (!state.getByIds.isEmpty() || !state.putByIds.isEmpty() || !state.checkIns.isEmpty()) { + if (!state.getByIds.isEmpty() || !state.putByIds.isEmpty()) { CCallHelpers slowPathJIT(&vm, codeBlock); - CCallHelpers::JumpList exceptionTarget; - for (unsigned i = state.getByIds.size(); i--;) { GetByIdDescriptor& getById = state.getByIds[i]; - if (verboseCompilationEnabled()) + if (Options::verboseCompilation()) dataLog("Handling GetById stackmap #", getById.stackmapID(), "\n"); - auto iter = recordMap.find(getById.stackmapID()); + StackMaps::RecordMap::iterator iter = recordMap.find(getById.stackmapID()); if (iter == recordMap.end()) { // It was optimized out. continue; } - for (unsigned i = 0; i < iter->value.size(); ++i) { - StackMaps::Record& record = iter->value[i]; + StackMaps::Record& record = iter->value; - RegisterSet usedRegisters = usedRegistersFor(record); - - GPRReg result = record.locations[0].directGPR(); - GPRReg base = record.locations[1].directGPR(); - - JITGetByIdGenerator gen( - codeBlock, getById.codeOrigin(), usedRegisters, JSValueRegs(base), - JSValueRegs(result), NeedToSpill); - - MacroAssembler::Label begin = slowPathJIT.label(); - - MacroAssembler::Call call = callOperation( - state, usedRegisters, slowPathJIT, getById.codeOrigin(), &exceptionTarget, - operationGetByIdOptimize, result, gen.stubInfo(), base, getById.uid()); - - gen.reportSlowPathCall(begin, call); - - getById.m_slowPathDone.append(slowPathJIT.jump()); - getById.m_generators.append(gen); - } + // 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 (verboseCompilationEnabled()) + if (Options::verboseCompilation()) dataLog("Handling PutById stackmap #", putById.stackmapID(), "\n"); - auto iter = recordMap.find(putById.stackmapID()); + StackMaps::RecordMap::iterator iter = recordMap.find(putById.stackmapID()); if (iter == recordMap.end()) { // It was optimized out. continue; } - for (unsigned i = 0; i < iter->value.size(); ++i) { - StackMaps::Record& record = iter->value[i]; - - RegisterSet usedRegisters = usedRegistersFor(record); - - GPRReg base = record.locations[0].directGPR(); - GPRReg value = record.locations[1].directGPR(); - - JITPutByIdGenerator gen( - codeBlock, putById.codeOrigin(), usedRegisters, JSValueRegs(base), - JSValueRegs(value), GPRInfo::patchpointScratchRegister, NeedToSpill, - putById.ecmaMode(), putById.putKind()); - - MacroAssembler::Label begin = slowPathJIT.label(); - - MacroAssembler::Call call = callOperation( - state, usedRegisters, slowPathJIT, putById.codeOrigin(), &exceptionTarget, - gen.slowPathFunction(), gen.stubInfo(), value, base, putById.uid()); - - gen.reportSlowPathCall(begin, call); - - putById.m_slowPathDone.append(slowPathJIT.jump()); - putById.m_generators.append(gen); - } - } - - for (unsigned i = state.checkIns.size(); i--;) { - CheckInDescriptor& checkIn = state.checkIns[i]; + StackMaps::Record& record = iter->value; - if (verboseCompilationEnabled()) - dataLog("Handling checkIn stackmap #", checkIn.stackmapID(), "\n"); + // FIXME: LLVM should tell us which registers are live. + RegisterSet usedRegisters = RegisterSet::allRegisters(); - auto iter = recordMap.find(checkIn.stackmapID()); - if (iter == recordMap.end()) { - // It was optimized out. - continue; - } + GPRReg callFrameRegister = record.locations[0].directGPR(); + GPRReg base = record.locations[1].directGPR(); + GPRReg value = record.locations[2].directGPR(); - for (unsigned i = 0; i < iter->value.size(); ++i) { - StackMaps::Record& record = iter->value[i]; - RegisterSet usedRegisters = usedRegistersFor(record); - GPRReg result = record.locations[0].directGPR(); - GPRReg obj = record.locations[1].directGPR(); - StructureStubInfo* stubInfo = codeBlock->addStubInfo(); - stubInfo->codeOrigin = checkIn.codeOrigin(); - stubInfo->patch.baseGPR = static_cast<int8_t>(obj); - stubInfo->patch.valueGPR = static_cast<int8_t>(result); - stubInfo->patch.usedRegisters = usedRegisters; - stubInfo->patch.spillMode = NeedToSpill; - - MacroAssembler::Label begin = slowPathJIT.label(); - - MacroAssembler::Call slowCall = callOperation( - state, usedRegisters, slowPathJIT, checkIn.codeOrigin(), &exceptionTarget, - operationInOptimize, result, stubInfo, obj, checkIn.m_uid); - - checkIn.m_slowPathDone.append(slowPathJIT.jump()); - - checkIn.m_generators.append(CheckInGenerator(stubInfo, slowCall, begin)); - } + 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; } - exceptionTarget.link(&slowPathJIT); - MacroAssembler::Jump exceptionJump = slowPathJIT.jump(); - - state.finalizer->sideCodeLinkBuffer = std::make_unique<LinkBuffer>(vm, slowPathJIT, codeBlock, JITCompilationCanFail); - if (state.finalizer->sideCodeLinkBuffer->didFailToAllocate()) { - state.allocationFailed = true; - return; - } - state.finalizer->sideCodeLinkBuffer->link( - exceptionJump, state.finalizer->handleExceptionsLinkBuffer->entrypoint()); + state.finalizer->sideCodeLinkBuffer = adoptPtr( + new LinkBuffer(vm, &slowPathJIT, codeBlock, JITCompilationMustSucceed)); for (unsigned i = state.getByIds.size(); i--;) { generateICFastPath( @@ -576,227 +272,108 @@ static void fixFunctionBasedOnStackMaps( state, codeBlock, generatedFunction, recordMap, state.putByIds[i], sizeOfPutById()); } - - for (unsigned i = state.checkIns.size(); i--;) { - generateCheckInICFastPath( - state, codeBlock, generatedFunction, recordMap, state.checkIns[i], - sizeOfIn()); - } - } - - adjustCallICsForStackmaps(state.jsCalls, recordMap); - - for (unsigned i = state.jsCalls.size(); i--;) { - JSCall& call = state.jsCalls[i]; - - CCallHelpers fastPathJIT(&vm, codeBlock); - call.emit(fastPathJIT); - - char* startOfIC = bitwise_cast<char*>(generatedFunction) + call.m_instructionOffset; - - generateInlineIfPossibleOutOfLineIfNot(state, vm, codeBlock, fastPathJIT, startOfIC, sizeOfCall(), "JSCall inline cache", [&] (LinkBuffer& linkBuffer, CCallHelpers&, bool) { - call.link(vm, linkBuffer); - }); - } - - adjustCallICsForStackmaps(state.jsCallVarargses, recordMap); - - for (unsigned i = state.jsCallVarargses.size(); i--;) { - JSCallVarargs& call = state.jsCallVarargses[i]; - - CCallHelpers fastPathJIT(&vm, codeBlock); - call.emit(fastPathJIT, varargsSpillSlotsOffset); - - char* startOfIC = bitwise_cast<char*>(generatedFunction) + call.m_instructionOffset; - size_t sizeOfIC = sizeOfICFor(call.node()); - - generateInlineIfPossibleOutOfLineIfNot(state, vm, codeBlock, fastPathJIT, startOfIC, sizeOfIC, "varargs call inline cache", [&] (LinkBuffer& linkBuffer, CCallHelpers&, bool) { - call.link(vm, linkBuffer, state.finalizer->handleExceptionsLinkBuffer->entrypoint()); - }); } RepatchBuffer repatchBuffer(codeBlock); - - auto iter = recordMap.find(state.handleStackOverflowExceptionStackmapID); - // It's sort of remotely possible that we won't have an in-band exception handling - // path, for some kinds of functions. - if (iter != recordMap.end()) { - for (unsigned i = iter->value.size(); i--;) { - StackMaps::Record& record = iter->value[i]; - - CodeLocationLabel source = CodeLocationLabel( - bitwise_cast<char*>(generatedFunction) + record.instructionOffset); - - RELEASE_ASSERT(stackOverflowException.isSet()); - - repatchBuffer.replaceWithJump(source, state.finalizer->handleExceptionsLinkBuffer->locationOf(stackOverflowException)); - } - } - - iter = recordMap.find(state.handleExceptionStackmapID); - // It's sort of remotely possible that we won't have an in-band exception handling - // path, for some kinds of functions. - if (iter != recordMap.end()) { - for (unsigned i = iter->value.size(); i--;) { - StackMaps::Record& record = iter->value[i]; - - CodeLocationLabel source = CodeLocationLabel( - bitwise_cast<char*>(generatedFunction) + record.instructionOffset); - - repatchBuffer.replaceWithJump(source, state.finalizer->handleExceptionsLinkBuffer->entrypoint()); - } - } - for (unsigned exitIndex = 0; exitIndex < jitCode->osrExit.size(); ++exitIndex) { + for (unsigned exitIndex = jitCode->osrExit.size(); exitIndex--;) { OSRExitCompilationInfo& info = state.finalizer->osrExit[exitIndex]; OSRExit& exit = jitCode->osrExit[exitIndex]; - iter = recordMap.find(exit.m_stackmapID); + StackMaps::RecordMap::iterator iter = recordMap.find(exit.m_stackmapID); + if (iter == recordMap.end()) { + // This could happen if LLVM optimizes out an OSR exit. + continue; + } - Vector<const void*> codeAddresses; + StackMaps::Record& record = iter->value; - if (iter != recordMap.end()) { - for (unsigned i = iter->value.size(); i--;) { - StackMaps::Record& record = iter->value[i]; - - CodeLocationLabel source = CodeLocationLabel( - bitwise_cast<char*>(generatedFunction) + record.instructionOffset); - - codeAddresses.append(bitwise_cast<char*>(generatedFunction) + record.instructionOffset + MacroAssembler::maxJumpReplacementSize()); - - if (info.m_isInvalidationPoint) - jitCode->common.jumpReplacements.append(JumpReplacement(source, info.m_thunkAddress)); - else - repatchBuffer.replaceWithJump(source, info.m_thunkAddress); - } + CodeLocationLabel source = CodeLocationLabel( + bitwise_cast<char*>(generatedFunction) + record.instructionOffset); + + if (info.m_isInvalidationPoint) { + jitCode->common.jumpReplacements.append(JumpReplacement(source, info.m_thunkAddress)); + continue; } - if (graph.compilation()) - graph.compilation()->addOSRExitSite(codeAddresses); + repatchBuffer.replaceWithJump(source, info.m_thunkAddress); } } -void compile(State& state, Safepoint::Result& safepointResult) +void compile(State& state) { char* error = 0; - { - GraphSafepoint safepoint(state.graph, safepointResult); - - LLVMMCJITCompilerOptions options; - llvm->InitializeMCJITCompilerOptions(&options, sizeof(options)); - options.OptLevel = Options::llvmBackendOptimizationLevel(); - options.NoFramePointerElim = true; - if (Options::useLLVMSmallCodeModel()) - options.CodeModel = LLVMCodeModelSmall; - options.EnableFastISel = enableLLVMFastISel; - options.MCJMM = llvm->CreateSimpleMCJITMemoryManager( - &state, mmAllocateCodeSection, mmAllocateDataSection, mmApplyPermissions, mmDestroy); + 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 (isARM64()) { -#if OS(DARWIN) - llvm->SetTarget(state.module, "arm64-apple-ios"); -#elif OS(LINUX) - llvm->SetTarget(state.module, "aarch64-linux-gnu"); -#else -#error "Unrecognized OS" -#endif - } - - if (llvm->CreateMCJITCompilerForModule(&engine, state.module, &options, sizeof(options), &error)) { - dataLog("FATAL: Could not create LLVM execution engine: ", error, "\n"); - CRASH(); - } - - // At this point we no longer own the module. - LModule module = state.module; - state.module = nullptr; - - // The data layout also has to be set in the module. Get the data layout from the MCJIT and apply - // it to the module. - LLVMTargetMachineRef targetMachine = llvm->GetExecutionEngineTargetMachine(engine); - LLVMTargetDataRef targetData = llvm->GetExecutionEngineTargetData(engine); - char* stringRepOfTargetData = llvm->CopyStringRepOfTargetData(targetData); - llvm->SetDataLayout(module, stringRepOfTargetData); - free(stringRepOfTargetData); - - LLVMPassManagerRef functionPasses = 0; - LLVMPassManagerRef modulePasses; - - if (Options::llvmSimpleOpt()) { - modulePasses = llvm->CreatePassManager(); - llvm->AddTargetData(targetData, modulePasses); - llvm->AddAnalysisPasses(targetMachine, modulePasses); - llvm->AddPromoteMemoryToRegisterPass(modulePasses); - llvm->AddGlobalOptimizerPass(modulePasses); - llvm->AddFunctionInliningPass(modulePasses); - llvm->AddPruneEHPass(modulePasses); - llvm->AddGlobalDCEPass(modulePasses); - llvm->AddConstantPropagationPass(modulePasses); - llvm->AddAggressiveDCEPass(modulePasses); - llvm->AddInstructionCombiningPass(modulePasses); - // BEGIN - DO NOT CHANGE THE ORDER OF THE ALIAS ANALYSIS PASSES - llvm->AddTypeBasedAliasAnalysisPass(modulePasses); - llvm->AddBasicAliasAnalysisPass(modulePasses); - // END - DO NOT CHANGE THE ORDER OF THE ALIAS ANALYSIS PASSES - llvm->AddGVNPass(modulePasses); - llvm->AddCFGSimplificationPass(modulePasses); - llvm->AddDeadStoreEliminationPass(modulePasses); - - if (enableLLVMFastISel) - llvm->AddLowerSwitchPass(modulePasses); - - llvm->RunPassManager(modulePasses, module); - } else { - LLVMPassManagerBuilderRef passBuilder = llvm->PassManagerBuilderCreate(); - llvm->PassManagerBuilderSetOptLevel(passBuilder, Options::llvmOptimizationLevel()); - llvm->PassManagerBuilderUseInlinerWithThreshold(passBuilder, 275); - llvm->PassManagerBuilderSetSizeLevel(passBuilder, Options::llvmSizeLevel()); - - functionPasses = llvm->CreateFunctionPassManagerForModule(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(module); function; function = llvm->GetNextFunction(function)) - llvm->RunFunctionPassManager(functionPasses, function); - llvm->FinalizeFunctionPassManager(functionPasses); - - llvm->RunPassManager(modulePasses, module); - } - - if (shouldShowDisassembly() || verboseCompilationEnabled()) - state.dumpState(module, "after optimization"); - - // 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); + LLVMExecutionEngineRef engine; + + if (llvm->CreateMCJITCompilerForModule(&engine, state.module, &options, sizeof(options), &error)) { + dataLog("FATAL: Could not create LLVM execution engine: ", error, "\n"); + CRASH(); } - if (safepointResult.didGetCancelled()) - return; - RELEASE_ASSERT(!state.graph.m_vm.heap.isCollecting()); + LLVMPassManagerRef functionPasses = 0; + LLVMPassManagerRef modulePasses; - if (state.allocationFailed) - return; + 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"); + // 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); + 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::FTLJIT), + CodeBlockWithJITType(state.graph.m_codeBlock, JITCode::DFGJIT), " #", i, ", ", state.codeSectionNames[i], ":\n"); disassemble( MacroAssemblerCodePtr(handle->start()), handle->sizeInBytes(), @@ -804,37 +381,26 @@ void compile(State& state, Safepoint::Result& safepointResult) } for (unsigned i = 0; i < state.jitCode->dataSections().size(); ++i) { - DataSection* section = state.jitCode->dataSections()[i].get(); + const RefCountedArray<LSectionWord>& section = state.jitCode->dataSections()[i]; dataLog( "Generated LLVM data section for ", - CodeBlockWithJITType(state.graph.m_codeBlock, JITCode::FTLJIT), + CodeBlockWithJITType(state.graph.m_codeBlock, JITCode::DFGJIT), " #", i, ", ", state.dataSectionNames[i], ":\n"); dumpDataSection(section, " "); } } - bool didSeeUnwindInfo = state.jitCode->unwindInfo.parse( - state.unwindDataSection, state.unwindDataSectionSize, - state.generatedFunction); - if (shouldShowDisassembly()) { - dataLog("Unwind info for ", CodeBlockWithJITType(state.graph.m_codeBlock, JITCode::FTLJIT), ":\n"); - if (didSeeUnwindInfo) - dataLog(" ", state.jitCode->unwindInfo, "\n"); - else - dataLog(" <no unwind info>\n"); - } - - if (state.stackmapsSection && state.stackmapsSection->size()) { + if (state.stackmapsSection.size()) { if (shouldShowDisassembly()) { dataLog( "Generated LLVM stackmaps section for ", - CodeBlockWithJITType(state.graph.m_codeBlock, JITCode::FTLJIT), ":\n"); + CodeBlockWithJITType(state.graph.m_codeBlock, JITCode::DFGJIT), ":\n"); dataLog(" Raw data:\n"); - dumpDataSection(state.stackmapsSection.get(), " "); + dumpDataSection(state.stackmapsSection, " "); } RefPtr<DataView> stackmapsData = DataView::create( - ArrayBuffer::create(state.stackmapsSection->base(), state.stackmapsSection->size())); + ArrayBuffer::create(state.stackmapsSection.data(), state.stackmapsSection.byteSize())); state.jitCode->stackmaps.parse(stackmapsData.get()); if (shouldShowDisassembly()) { @@ -842,40 +408,29 @@ void compile(State& state, Safepoint::Result& safepointResult) state.jitCode->stackmaps.dumpMultiline(WTF::dataFile(), " "); } - StackMaps::RecordMap recordMap = state.jitCode->stackmaps.computeRecordMap(); + StackMaps::RecordMap recordMap = state.jitCode->stackmaps.getRecordMap(); fixFunctionBasedOnStackMaps( state, state.graph.m_codeBlock, state.jitCode.get(), state.generatedFunction, - recordMap, didSeeUnwindInfo); - if (state.allocationFailed) - return; + recordMap); - if (shouldShowDisassembly() || Options::asyncDisassembly()) { + if (shouldShowDisassembly()) { for (unsigned i = 0; i < state.jitCode->handles().size(); ++i) { - if (state.codeSectionNames[i] != SECTION_NAME("text")) + if (state.codeSectionNames[i] != "__text") continue; ExecutableMemoryHandle* handle = state.jitCode->handles()[i].get(); - - CString header = toCString( + dataLog( "Generated LLVM code after stackmap-based fix-up for ", - CodeBlockWithJITType(state.graph.m_codeBlock, JITCode::FTLJIT), - " in ", state.graph.m_plan.mode, " #", i, ", ", - state.codeSectionNames[i], ":\n"); - - if (Options::asyncDisassembly()) { - disassembleAsynchronously( - header, MacroAssemblerCodeRef(handle), handle->sizeInBytes(), " ", - LLVMSubset); - continue; - } - - dataLog(header); + 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 |
