[WTF] Add makeUnique<T>, which ensures T is fast-allocated, makeUnique / makeUniqueWi...
[WebKit-https.git] / Source / JavaScriptCore / bytecode / BytecodeBasicBlock.cpp
1 /*
2  * Copyright (C) 2013-2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "BytecodeBasicBlock.h"
28
29 #include "CodeBlock.h"
30 #include "InterpreterInlines.h"
31 #include "JSCInlines.h"
32 #include "PreciseJumpTargets.h"
33
34 namespace JSC {
35
36 void BytecodeBasicBlock::shrinkToFit()
37 {
38     m_offsets.shrinkToFit();
39     m_successors.shrinkToFit();
40 }
41
42 static bool isJumpTarget(OpcodeID opcodeID, const Vector<InstructionStream::Offset, 32>& jumpTargets, unsigned bytecodeOffset)
43 {
44     if (opcodeID == op_catch)
45         return true;
46
47     return std::binary_search(jumpTargets.begin(), jumpTargets.end(), bytecodeOffset);
48 }
49
50 template<typename Block>
51 void BytecodeBasicBlock::computeImpl(Block* codeBlock, const InstructionStream& instructions, Vector<std::unique_ptr<BytecodeBasicBlock>>& basicBlocks)
52 {
53     Vector<InstructionStream::Offset, 32> jumpTargets;
54     computePreciseJumpTargets(codeBlock, instructions, jumpTargets);
55
56     auto appendBlock = [&] (std::unique_ptr<BytecodeBasicBlock>&& block) {
57         block->m_index = basicBlocks.size();
58         basicBlocks.append(WTFMove(block));
59     };
60
61     auto linkBlocks = [&] (BytecodeBasicBlock* from, BytecodeBasicBlock* to) {
62         from->addSuccessor(to);
63     };
64
65     // Create the entry and exit basic blocks.
66     basicBlocks.reserveCapacity(jumpTargets.size() + 2);
67
68     auto entry = makeUnique<BytecodeBasicBlock>(BytecodeBasicBlock::EntryBlock);
69     auto firstBlock = makeUnique<BytecodeBasicBlock>(BytecodeBasicBlock::EntryBlock);
70     linkBlocks(entry.get(), firstBlock.get());
71
72     appendBlock(WTFMove(entry));
73     BytecodeBasicBlock* current = firstBlock.get();
74     appendBlock(WTFMove(firstBlock));
75
76     auto exit = makeUnique<BytecodeBasicBlock>(BytecodeBasicBlock::ExitBlock);
77
78     bool nextInstructionIsLeader = false;
79
80     for (const auto& instruction : instructions) {
81         auto bytecodeOffset = instruction.offset();
82         OpcodeID opcodeID = instruction->opcodeID();
83
84         bool createdBlock = false;
85         // If the current bytecode is a jump target, then it's the leader of its own basic block.
86         if (isJumpTarget(opcodeID, jumpTargets, bytecodeOffset) || nextInstructionIsLeader) {
87             auto newBlock = makeUnique<BytecodeBasicBlock>(instruction);
88             current = newBlock.get();
89             appendBlock(WTFMove(newBlock));
90             createdBlock = true;
91             nextInstructionIsLeader = false;
92         }
93
94         // If the current bytecode is a branch or a return, then the next instruction is the leader of its own basic block.
95         if (isBranch(opcodeID) || isTerminal(opcodeID) || isThrow(opcodeID))
96             nextInstructionIsLeader = true;
97
98         if (createdBlock)
99             continue;
100
101         // Otherwise, just add to the length of the current block.
102         current->addLength(instruction->size());
103     }
104
105     // Link basic blocks together.
106     for (unsigned i = 0; i < basicBlocks.size(); i++) {
107         BytecodeBasicBlock* block = basicBlocks[i].get();
108
109         if (block->isEntryBlock() || block->isExitBlock())
110             continue;
111
112         bool fallsThrough = true;
113         for (auto bytecodeOffset : block->offsets()) {
114             auto instruction = instructions.at(bytecodeOffset);
115             OpcodeID opcodeID = instruction->opcodeID();
116
117             // If we found a terminal bytecode, link to the exit block.
118             if (isTerminal(opcodeID)) {
119                 ASSERT(bytecodeOffset + instruction->size() == block->leaderOffset() + block->totalLength());
120                 linkBlocks(block, exit.get());
121                 fallsThrough = false;
122                 break;
123             }
124
125             // If we found a throw, get the HandlerInfo for this instruction to see where we will jump.
126             // If there isn't one, treat this throw as a terminal. This is true even if we have a finally
127             // block because the finally block will create its own catch, which will generate a HandlerInfo.
128             if (isThrow(opcodeID)) {
129                 ASSERT(bytecodeOffset + instruction->size() == block->leaderOffset() + block->totalLength());
130                 auto* handler = codeBlock->handlerForBytecodeOffset(instruction.offset());
131                 fallsThrough = false;
132                 if (!handler) {
133                     linkBlocks(block, exit.get());
134                     break;
135                 }
136                 for (unsigned i = 0; i < basicBlocks.size(); i++) {
137                     BytecodeBasicBlock* otherBlock = basicBlocks[i].get();
138                     if (handler->target == otherBlock->leaderOffset()) {
139                         linkBlocks(block, otherBlock);
140                         break;
141                     }
142                 }
143                 break;
144             }
145
146             // If we found a branch, link to the block(s) that we jump to.
147             if (isBranch(opcodeID)) {
148                 ASSERT(bytecodeOffset + instruction->size() == block->leaderOffset() + block->totalLength());
149                 Vector<InstructionStream::Offset, 1> bytecodeOffsetsJumpedTo;
150                 findJumpTargetsForInstruction(codeBlock, instruction, bytecodeOffsetsJumpedTo);
151
152                 size_t numberOfJumpTargets = bytecodeOffsetsJumpedTo.size();
153                 ASSERT(numberOfJumpTargets);
154                 for (unsigned i = 0; i < basicBlocks.size(); i++) {
155                     BytecodeBasicBlock* otherBlock = basicBlocks[i].get();
156                     if (bytecodeOffsetsJumpedTo.contains(otherBlock->leaderOffset())) {
157                         linkBlocks(block, otherBlock);
158                         --numberOfJumpTargets;
159                         if (!numberOfJumpTargets)
160                             break;
161                     }
162                 }
163                 // numberOfJumpTargets may not be 0 here if there are multiple jumps targeting the same
164                 // basic blocks (e.g. in a switch type opcode). Since we only decrement numberOfJumpTargets
165                 // once per basic block, the duplicates are not accounted for. For our purpose here,
166                 // that doesn't matter because we only need to link to the target block once regardless
167                 // of how many ways this block can jump there.
168
169                 if (isUnconditionalBranch(opcodeID))
170                     fallsThrough = false;
171
172                 break;
173             }
174         }
175
176         // If we fall through then link to the next block in program order.
177         if (fallsThrough) {
178             ASSERT(i + 1 < basicBlocks.size());
179             BytecodeBasicBlock* nextBlock = basicBlocks[i + 1].get();
180             linkBlocks(block, nextBlock);
181         }
182     }
183
184     appendBlock(WTFMove(exit));
185
186     for (auto& basicBlock : basicBlocks)
187         basicBlock->shrinkToFit();
188 }
189
190 void BytecodeBasicBlock::compute(CodeBlock* codeBlock, const InstructionStream& instructions, Vector<std::unique_ptr<BytecodeBasicBlock>>& basicBlocks)
191 {
192     computeImpl(codeBlock, instructions, basicBlocks);
193 }
194
195 void BytecodeBasicBlock::compute(UnlinkedCodeBlock* codeBlock, const InstructionStream& instructions, Vector<std::unique_ptr<BytecodeBasicBlock>>& basicBlocks)
196 {
197     computeImpl(codeBlock, instructions, basicBlocks);
198 }
199
200 } // namespace JSC