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
34namespace JSC {
35
36void BytecodeBasicBlock::shrinkToFit()
37{
38 m_offsets.shrinkToFit();
39 m_successors.shrinkToFit();
40}
41
42static 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
50template<typename Block>
51void 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
190void BytecodeBasicBlock::compute(CodeBlock* codeBlock, const InstructionStream& instructions, Vector<std::unique_ptr<BytecodeBasicBlock>>& basicBlocks)
191{
192 computeImpl(codeBlock, instructions, basicBlocks);
193}
194
195void 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