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 = std::make_unique<BytecodeBasicBlock>(BytecodeBasicBlock::EntryBlock); |
69 | auto firstBlock = std::make_unique<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 = std::make_unique<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 = std::make_unique<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 |
201 | |