diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/JavaScriptCore/bytecode/BytecodeGeneratorification.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/JavaScriptCore/bytecode/BytecodeGeneratorification.cpp')
-rw-r--r-- | Source/JavaScriptCore/bytecode/BytecodeGeneratorification.cpp | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/bytecode/BytecodeGeneratorification.cpp b/Source/JavaScriptCore/bytecode/BytecodeGeneratorification.cpp new file mode 100644 index 000000000..f7e1e9a3d --- /dev/null +++ b/Source/JavaScriptCore/bytecode/BytecodeGeneratorification.cpp @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2016 Yusuke Suzuki <utatane.tea@gmail.com> + * Copyright (C) 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. AND ITS CONTRIBUTORS ``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 ITS 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 "BytecodeGeneratorification.h" + +#include "BytecodeLivenessAnalysisInlines.h" +#include "BytecodeRewriter.h" +#include "BytecodeUseDef.h" +#include "IdentifierInlines.h" +#include "InterpreterInlines.h" +#include "JSCInlines.h" +#include "JSCJSValueInlines.h" +#include "JSGeneratorFunction.h" +#include "StrongInlines.h" +#include "UnlinkedCodeBlock.h" +#include <wtf/Optional.h> + +namespace JSC { + +struct YieldData { + size_t point { 0 }; + int argument { 0 }; + FastBitVector liveness; +}; + +class BytecodeGeneratorification { +public: + typedef Vector<YieldData> Yields; + + BytecodeGeneratorification(UnlinkedCodeBlock* codeBlock, UnlinkedCodeBlock::UnpackedInstructions& instructions, SymbolTable* generatorFrameSymbolTable, int generatorFrameSymbolTableIndex) + : m_graph(codeBlock, instructions) + , m_generatorFrameSymbolTable(*codeBlock->vm(), generatorFrameSymbolTable) + , m_generatorFrameSymbolTableIndex(generatorFrameSymbolTableIndex) + { + for (BytecodeBasicBlock* block : m_graph) { + for (unsigned bytecodeOffset : block->offsets()) { + const UnlinkedInstruction* pc = &m_graph.instructions()[bytecodeOffset]; + switch (pc->u.opcode) { + case op_enter: { + m_enterPoint = bytecodeOffset; + break; + } + + case op_yield: { + unsigned liveCalleeLocalsIndex = pc[2].u.index; + if (liveCalleeLocalsIndex >= m_yields.size()) + m_yields.resize(liveCalleeLocalsIndex + 1); + YieldData& data = m_yields[liveCalleeLocalsIndex]; + data.point = bytecodeOffset; + data.argument = pc[3].u.operand; + break; + } + + default: + break; + } + } + } + } + + struct Storage { + Identifier identifier; + unsigned identifierIndex; + ScopeOffset scopeOffset; + }; + + void run(); + + BytecodeGraph<UnlinkedCodeBlock>& graph() { return m_graph; } + + const Yields& yields() const + { + return m_yields; + } + + Yields& yields() + { + return m_yields; + } + + unsigned enterPoint() const + { + return m_enterPoint; + } + +private: + Storage storageForGeneratorLocal(unsigned index) + { + // We assign a symbol to a register. There is one-on-one corresponding between a register and a symbol. + // By doing so, we allocate the specific storage to save the given register. + // This allow us not to save all the live registers even if the registers are not overwritten from the previous resuming time. + // It means that, the register can be retrieved even if the immediate previous op_save does not save it. + + if (m_storages.size() <= index) + m_storages.resize(index + 1); + if (std::optional<Storage> storage = m_storages[index]) + return *storage; + + UnlinkedCodeBlock* codeBlock = m_graph.codeBlock(); + Identifier identifier = Identifier::fromUid(PrivateName()); + unsigned identifierIndex = codeBlock->numberOfIdentifiers(); + codeBlock->addIdentifier(identifier); + ScopeOffset scopeOffset = m_generatorFrameSymbolTable->takeNextScopeOffset(NoLockingNecessary); + m_generatorFrameSymbolTable->set(NoLockingNecessary, identifier.impl(), SymbolTableEntry(VarOffset(scopeOffset))); + + Storage storage = { + identifier, + identifierIndex, + scopeOffset + }; + m_storages[index] = storage; + return storage; + } + + unsigned m_enterPoint { 0 }; + BytecodeGraph<UnlinkedCodeBlock> m_graph; + Vector<std::optional<Storage>> m_storages; + Yields m_yields; + Strong<SymbolTable> m_generatorFrameSymbolTable; + int m_generatorFrameSymbolTableIndex; +}; + +class GeneratorLivenessAnalysis : public BytecodeLivenessPropagation<GeneratorLivenessAnalysis> { +public: + GeneratorLivenessAnalysis(BytecodeGeneratorification& generatorification) + : m_generatorification(generatorification) + { + } + + template<typename Functor> + void computeDefsForBytecodeOffset(UnlinkedCodeBlock* codeBlock, OpcodeID opcodeID, UnlinkedInstruction* instruction, FastBitVector&, const Functor& functor) + { + JSC::computeDefsForBytecodeOffset(codeBlock, opcodeID, instruction, functor); + } + + template<typename Functor> + void computeUsesForBytecodeOffset(UnlinkedCodeBlock* codeBlock, OpcodeID opcodeID, UnlinkedInstruction* instruction, FastBitVector&, const Functor& functor) + { + JSC::computeUsesForBytecodeOffset(codeBlock, opcodeID, instruction, functor); + } + + void run() + { + // Perform modified liveness analysis to determine which locals are live at the merge points. + // This produces the conservative results for the question, "which variables should be saved and resumed?". + + runLivenessFixpoint(m_generatorification.graph()); + + for (YieldData& data : m_generatorification.yields()) + data.liveness = getLivenessInfoAtBytecodeOffset(m_generatorification.graph(), data.point + opcodeLength(op_yield)); + } + +private: + BytecodeGeneratorification& m_generatorification; +}; + +void BytecodeGeneratorification::run() +{ + // We calculate the liveness at each merge point. This gives us the information which registers should be saved and resumed conservatively. + + { + GeneratorLivenessAnalysis pass(*this); + pass.run(); + } + + UnlinkedCodeBlock* codeBlock = m_graph.codeBlock(); + BytecodeRewriter rewriter(m_graph); + + // Setup the global switch for the generator. + { + unsigned nextToEnterPoint = enterPoint() + opcodeLength(op_enter); + unsigned switchTableIndex = m_graph.codeBlock()->numberOfSwitchJumpTables(); + VirtualRegister state = virtualRegisterForArgument(static_cast<int32_t>(JSGeneratorFunction::GeneratorArgument::State)); + auto& jumpTable = m_graph.codeBlock()->addSwitchJumpTable(); + jumpTable.min = 0; + jumpTable.branchOffsets.resize(m_yields.size() + 1); + jumpTable.branchOffsets.fill(0); + jumpTable.add(0, nextToEnterPoint); + for (unsigned i = 0; i < m_yields.size(); ++i) + jumpTable.add(i + 1, m_yields[i].point); + + rewriter.insertFragmentBefore(nextToEnterPoint, [&](BytecodeRewriter::Fragment& fragment) { + fragment.appendInstruction(op_switch_imm, switchTableIndex, nextToEnterPoint, state.offset()); + }); + } + + for (const YieldData& data : m_yields) { + VirtualRegister scope = virtualRegisterForArgument(static_cast<int32_t>(JSGeneratorFunction::GeneratorArgument::Frame)); + + // Emit save sequence. + rewriter.insertFragmentBefore(data.point, [&](BytecodeRewriter::Fragment& fragment) { + data.liveness.forEachSetBit([&](size_t index) { + VirtualRegister operand = virtualRegisterForLocal(index); + Storage storage = storageForGeneratorLocal(index); + + fragment.appendInstruction( + op_put_to_scope, + scope.offset(), // scope + storage.identifierIndex, // identifier + operand.offset(), // value + GetPutInfo(DoNotThrowIfNotFound, LocalClosureVar, InitializationMode::NotInitialization).operand(), // info + m_generatorFrameSymbolTableIndex, // symbol table constant index + storage.scopeOffset.offset() // scope offset + ); + }); + + // Insert op_ret just after save sequence. + fragment.appendInstruction(op_ret, data.argument); + }); + + // Emit resume sequence. + rewriter.insertFragmentAfter(data.point, [&](BytecodeRewriter::Fragment& fragment) { + data.liveness.forEachSetBit([&](size_t index) { + VirtualRegister operand = virtualRegisterForLocal(index); + Storage storage = storageForGeneratorLocal(index); + + UnlinkedValueProfile profile = codeBlock->addValueProfile(); + fragment.appendInstruction( + op_get_from_scope, + operand.offset(), // dst + scope.offset(), // scope + storage.identifierIndex, // identifier + GetPutInfo(DoNotThrowIfNotFound, LocalClosureVar, InitializationMode::NotInitialization).operand(), // info + 0, // local scope depth + storage.scopeOffset.offset(), // scope offset + profile // profile + ); + }); + }); + + // Clip the unnecessary bytecodes. + rewriter.removeBytecode(data.point); + } + + rewriter.execute(); +} + +void performGeneratorification(UnlinkedCodeBlock* codeBlock, UnlinkedCodeBlock::UnpackedInstructions& instructions, SymbolTable* generatorFrameSymbolTable, int generatorFrameSymbolTableIndex) +{ + BytecodeGeneratorification pass(codeBlock, instructions, generatorFrameSymbolTable, generatorFrameSymbolTableIndex); + pass.run(); +} + +} // namespace JSC |