| /* |
| * Copyright (C) 2015-2016 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 "DFGLiveCatchVariablePreservationPhase.h" |
| |
| #if ENABLE(DFG_JIT) |
| |
| #include "DFGBasicBlockInlines.h" |
| #include "DFGBlockSet.h" |
| #include "DFGGraph.h" |
| #include "DFGInsertionSet.h" |
| #include "DFGPhase.h" |
| #include "FullBytecodeLiveness.h" |
| #include "JSCInlines.h" |
| |
| namespace JSC { namespace DFG { |
| |
| class LiveCatchVariablePreservationPhase : public Phase { |
| public: |
| LiveCatchVariablePreservationPhase(Graph& graph) |
| : Phase(graph, "live catch variable preservation phase") |
| { |
| } |
| |
| bool run() |
| { |
| DFG_ASSERT(m_graph, nullptr, m_graph.m_form == LoadStore); |
| |
| if (!m_graph.m_hasExceptionHandlers) |
| return false; |
| |
| InsertionSet insertionSet(m_graph); |
| if (m_graph.m_hasExceptionHandlers) { |
| for (BasicBlock* block : m_graph.blocksInNaturalOrder()) { |
| handleBlockForTryCatch(block, insertionSet); |
| insertionSet.execute(block); |
| } |
| } |
| |
| return true; |
| } |
| |
| bool isValidFlushLocation(BasicBlock* startingBlock, unsigned index, VirtualRegister operand) |
| { |
| // This code is not meant to be fast. We just use it for assertions. If we got liveness wrong, |
| // this function would return false for a Flush that we insert. |
| Vector<BasicBlock*, 4> worklist; |
| BlockSet seen; |
| |
| auto addPredecessors = [&] (BasicBlock* block) { |
| for (BasicBlock* predecessor : block->predecessors) { |
| bool isNewEntry = seen.add(predecessor); |
| if (isNewEntry) |
| worklist.append(predecessor); |
| } |
| }; |
| |
| auto flushIsDefinitelyInvalid = [&] (BasicBlock* block, unsigned index) { |
| bool allGood = false; |
| for (unsigned i = index; i--; ) { |
| if (block->at(i)->accessesStack(m_graph) && block->at(i)->local() == operand) { |
| allGood = true; |
| break; |
| } |
| } |
| |
| if (allGood) |
| return false; |
| |
| if (block->predecessors.isEmpty()) { |
| // This is a root block. We proved we reached here, therefore we can't Flush, as |
| // it'll make this local live at the start of a root block, which is invalid IR. |
| return true; |
| } |
| |
| addPredecessors(block); |
| return false; |
| }; |
| |
| if (flushIsDefinitelyInvalid(startingBlock, index)) |
| return false; |
| |
| while (!worklist.isEmpty()) { |
| BasicBlock* block = worklist.takeLast(); |
| if (flushIsDefinitelyInvalid(block, block->size())) |
| return false; |
| } |
| return true; |
| } |
| |
| |
| void handleBlockForTryCatch(BasicBlock* block, InsertionSet& insertionSet) |
| { |
| HandlerInfo* currentExceptionHandler = nullptr; |
| FastBitVector liveAtCatchHead; |
| liveAtCatchHead.resize(m_graph.block(0)->variablesAtTail.numberOfLocals()); |
| |
| HandlerInfo* cachedHandlerResult; |
| CodeOrigin cachedCodeOrigin; |
| auto catchHandler = [&] (CodeOrigin origin) -> HandlerInfo* { |
| ASSERT(origin); |
| if (origin == cachedCodeOrigin) |
| return cachedHandlerResult; |
| |
| BytecodeIndex bytecodeIndexToCheck = origin.bytecodeIndex(); |
| |
| cachedCodeOrigin = origin; |
| |
| while (1) { |
| InlineCallFrame* inlineCallFrame = origin.inlineCallFrame(); |
| CodeBlock* codeBlock = m_graph.baselineCodeBlockFor(inlineCallFrame); |
| if (HandlerInfo* handler = codeBlock->handlerForBytecodeIndex(bytecodeIndexToCheck)) { |
| liveAtCatchHead.clearAll(); |
| |
| BytecodeIndex catchBytecodeIndex = BytecodeIndex(handler->target); |
| m_graph.forAllLocalsLiveInBytecode(CodeOrigin(catchBytecodeIndex, inlineCallFrame), [&] (VirtualRegister operand) { |
| liveAtCatchHead[operand.toLocal()] = true; |
| }); |
| |
| cachedHandlerResult = handler; |
| break; |
| } |
| |
| if (!inlineCallFrame) { |
| cachedHandlerResult = nullptr; |
| break; |
| } |
| |
| bytecodeIndexToCheck = inlineCallFrame->directCaller.bytecodeIndex(); |
| origin = inlineCallFrame->directCaller; |
| } |
| |
| return cachedHandlerResult; |
| }; |
| |
| Operands<VariableAccessData*> currentBlockAccessData(block->variablesAtTail.numberOfArguments(), block->variablesAtTail.numberOfLocals(), nullptr); |
| |
| auto flushEverything = [&] (NodeOrigin origin, unsigned index) { |
| RELEASE_ASSERT(currentExceptionHandler); |
| auto flush = [&] (VirtualRegister operand) { |
| if ((operand.isLocal() && liveAtCatchHead[operand.toLocal()]) || operand.isArgument()) { |
| |
| ASSERT(isValidFlushLocation(block, index, operand)); |
| |
| VariableAccessData* accessData = currentBlockAccessData.operand(operand); |
| if (!accessData) |
| accessData = newVariableAccessData(operand); |
| |
| currentBlockAccessData.operand(operand) = accessData; |
| |
| insertionSet.insertNode(index, SpecNone, |
| Flush, origin, OpInfo(accessData)); |
| } |
| }; |
| |
| for (unsigned local = 0; local < block->variablesAtTail.numberOfLocals(); local++) |
| flush(virtualRegisterForLocal(local)); |
| flush(VirtualRegister(CallFrame::thisArgumentOffset())); |
| }; |
| |
| for (unsigned nodeIndex = 0; nodeIndex < block->size(); nodeIndex++) { |
| Node* node = block->at(nodeIndex); |
| |
| { |
| HandlerInfo* newHandler = catchHandler(node->origin.semantic); |
| if (newHandler != currentExceptionHandler && currentExceptionHandler) |
| flushEverything(node->origin, nodeIndex); |
| currentExceptionHandler = newHandler; |
| } |
| |
| if (currentExceptionHandler && (node->op() == SetLocal || node->op() == SetArgumentDefinitely || node->op() == SetArgumentMaybe)) { |
| VirtualRegister operand = node->local(); |
| if ((operand.isLocal() && liveAtCatchHead[operand.toLocal()]) || operand.isArgument()) { |
| |
| ASSERT(isValidFlushLocation(block, nodeIndex, operand)); |
| |
| VariableAccessData* variableAccessData = currentBlockAccessData.operand(operand); |
| if (!variableAccessData) |
| variableAccessData = newVariableAccessData(operand); |
| |
| insertionSet.insertNode(nodeIndex, SpecNone, |
| Flush, node->origin, OpInfo(variableAccessData)); |
| } |
| } |
| |
| if (node->accessesStack(m_graph)) |
| currentBlockAccessData.operand(node->local()) = node->variableAccessData(); |
| } |
| |
| if (currentExceptionHandler) { |
| NodeOrigin origin = block->at(block->size() - 1)->origin; |
| flushEverything(origin, block->size()); |
| } |
| } |
| |
| VariableAccessData* newVariableAccessData(VirtualRegister operand) |
| { |
| ASSERT(!operand.isConstant()); |
| |
| m_graph.m_variableAccessData.append(operand); |
| return &m_graph.m_variableAccessData.last(); |
| } |
| }; |
| |
| bool performLiveCatchVariablePreservationPhase(Graph& graph) |
| { |
| return runPhase<LiveCatchVariablePreservationPhase>(graph); |
| } |
| |
| } } // namespace JSC::DFG |
| |
| #endif // ENABLE(DFG_JIT) |