1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/interpreter/bytecode-peephole-optimizer.h"
6
7 #include "src/objects-inl.h"
8 #include "src/objects.h"
9
10 namespace v8 {
11 namespace internal {
12 namespace interpreter {
13
BytecodePeepholeOptimizer(BytecodePipelineStage * next_stage)14 BytecodePeepholeOptimizer::BytecodePeepholeOptimizer(
15 BytecodePipelineStage* next_stage)
16 : next_stage_(next_stage), last_(Bytecode::kIllegal, BytecodeSourceInfo()) {
17 InvalidateLast();
18 }
19
20 // override
ToBytecodeArray(Isolate * isolate,int register_count,int parameter_count,Handle<FixedArray> handler_table)21 Handle<BytecodeArray> BytecodePeepholeOptimizer::ToBytecodeArray(
22 Isolate* isolate, int register_count, int parameter_count,
23 Handle<FixedArray> handler_table) {
24 Flush();
25 return next_stage_->ToBytecodeArray(isolate, register_count, parameter_count,
26 handler_table);
27 }
28
29 // override
BindLabel(BytecodeLabel * label)30 void BytecodePeepholeOptimizer::BindLabel(BytecodeLabel* label) {
31 Flush();
32 next_stage_->BindLabel(label);
33 }
34
35 // override
BindLabel(const BytecodeLabel & target,BytecodeLabel * label)36 void BytecodePeepholeOptimizer::BindLabel(const BytecodeLabel& target,
37 BytecodeLabel* label) {
38 // There is no need to flush here, it will have been flushed when
39 // |target| was bound.
40 next_stage_->BindLabel(target, label);
41 }
42
43 // override
WriteJump(BytecodeNode * node,BytecodeLabel * label)44 void BytecodePeepholeOptimizer::WriteJump(BytecodeNode* node,
45 BytecodeLabel* label) {
46 // Handlers for jump bytecodes do not emit |node| as WriteJump()
47 // requires the |label| and having a label argument in all action
48 // handlers results in dead work in the non-jump case.
49 ApplyPeepholeAction(node);
50 next_stage()->WriteJump(node, label);
51 }
52
53 // override
Write(BytecodeNode * node)54 void BytecodePeepholeOptimizer::Write(BytecodeNode* node) {
55 // Handlers for non-jump bytecodes run to completion emitting
56 // bytecode to next stage as appropriate.
57 ApplyPeepholeAction(node);
58 }
59
Flush()60 void BytecodePeepholeOptimizer::Flush() {
61 if (LastIsValid()) {
62 next_stage_->Write(&last_);
63 InvalidateLast();
64 }
65 }
66
InvalidateLast()67 void BytecodePeepholeOptimizer::InvalidateLast() {
68 last_.set_bytecode(Bytecode::kIllegal);
69 }
70
LastIsValid() const71 bool BytecodePeepholeOptimizer::LastIsValid() const {
72 return last_.bytecode() != Bytecode::kIllegal;
73 }
74
SetLast(const BytecodeNode * const node)75 void BytecodePeepholeOptimizer::SetLast(const BytecodeNode* const node) {
76 // An action shouldn't leave a NOP as last bytecode unless it has
77 // source position information. NOP without source information can
78 // always be elided.
79 DCHECK(node->bytecode() != Bytecode::kNop || node->source_info().is_valid());
80 last_ = *node;
81 }
82
CanElideLastBasedOnSourcePosition(const BytecodeNode * const current) const83 bool BytecodePeepholeOptimizer::CanElideLastBasedOnSourcePosition(
84 const BytecodeNode* const current) const {
85 //
86 // The rules for allowing the elision of the last bytecode based
87 // on source position are:
88 //
89 // C U R R E N T
90 // +--------+--------+--------+
91 // | None | Expr | Stmt |
92 // L +--------+--------+--------+--------+
93 // | None | YES | YES | YES |
94 // A +--------+--------+--------+--------+
95 // | Expr | YES | MAYBE | MAYBE |
96 // S +--------+--------+--------+--------+
97 // | Stmt | YES | NO | NO |
98 // T +--------+--------+--------+--------+
99 //
100 // The goal is not lose any statement positions and not lose useful
101 // expression positions. Whenever the last bytecode is elided it's
102 // source position information is applied to the current node
103 // updating it if necessary.
104 //
105 // The last bytecode could be elided for the MAYBE cases if the last
106 // bytecode is known not to throw. If it throws, the system would
107 // not have correct stack trace information. The appropriate check
108 // for this would be Bytecodes::IsWithoutExternalSideEffects(). By
109 // default, the upstream bytecode generator filters out unneeded
110 // expression position information so there is neglible benefit to
111 // handling MAYBE specially. Hence MAYBE is treated the same as NO.
112 //
113 return (!last_.source_info().is_valid() ||
114 !current->source_info().is_valid());
115 }
116
117 namespace {
118
TransformLdaSmiBinaryOpToBinaryOpWithSmi(Bytecode new_bytecode,BytecodeNode * const last,BytecodeNode * const current)119 void TransformLdaSmiBinaryOpToBinaryOpWithSmi(Bytecode new_bytecode,
120 BytecodeNode* const last,
121 BytecodeNode* const current) {
122 DCHECK_EQ(last->bytecode(), Bytecode::kLdaSmi);
123 current->set_bytecode(new_bytecode, last->operand(0), current->operand(0),
124 current->operand(1));
125 if (last->source_info().is_valid()) {
126 current->set_source_info(last->source_info());
127 }
128 }
129
TransformLdaZeroBinaryOpToBinaryOpWithZero(Bytecode new_bytecode,BytecodeNode * const last,BytecodeNode * const current)130 void TransformLdaZeroBinaryOpToBinaryOpWithZero(Bytecode new_bytecode,
131 BytecodeNode* const last,
132 BytecodeNode* const current) {
133 DCHECK_EQ(last->bytecode(), Bytecode::kLdaZero);
134 current->set_bytecode(new_bytecode, 0, current->operand(0),
135 current->operand(1));
136 if (last->source_info().is_valid()) {
137 current->set_source_info(last->source_info());
138 }
139 }
140
141 } // namespace
142
DefaultAction(BytecodeNode * const node,const PeepholeActionAndData * action_data)143 void BytecodePeepholeOptimizer::DefaultAction(
144 BytecodeNode* const node, const PeepholeActionAndData* action_data) {
145 DCHECK(LastIsValid());
146 DCHECK(!Bytecodes::IsJump(node->bytecode()));
147
148 next_stage()->Write(last());
149 SetLast(node);
150 }
151
UpdateLastAction(BytecodeNode * const node,const PeepholeActionAndData * action_data)152 void BytecodePeepholeOptimizer::UpdateLastAction(
153 BytecodeNode* const node, const PeepholeActionAndData* action_data) {
154 DCHECK(!LastIsValid());
155 DCHECK(!Bytecodes::IsJump(node->bytecode()));
156
157 SetLast(node);
158 }
159
UpdateLastIfSourceInfoPresentAction(BytecodeNode * const node,const PeepholeActionAndData * action_data)160 void BytecodePeepholeOptimizer::UpdateLastIfSourceInfoPresentAction(
161 BytecodeNode* const node, const PeepholeActionAndData* action_data) {
162 DCHECK(!LastIsValid());
163 DCHECK(!Bytecodes::IsJump(node->bytecode()));
164
165 if (node->source_info().is_valid()) {
166 SetLast(node);
167 }
168 }
169
ElideCurrentAction(BytecodeNode * const node,const PeepholeActionAndData * action_data)170 void BytecodePeepholeOptimizer::ElideCurrentAction(
171 BytecodeNode* const node, const PeepholeActionAndData* action_data) {
172 DCHECK(LastIsValid());
173 DCHECK(!Bytecodes::IsJump(node->bytecode()));
174
175 if (node->source_info().is_valid()) {
176 // Preserve the source information by replacing the node bytecode
177 // with a no op bytecode.
178 node->set_bytecode(Bytecode::kNop);
179 DefaultAction(node);
180 } else {
181 // Nothing to do, keep last and wait for next bytecode to pair with it.
182 }
183 }
184
ElideCurrentIfOperand0MatchesAction(BytecodeNode * const node,const PeepholeActionAndData * action_data)185 void BytecodePeepholeOptimizer::ElideCurrentIfOperand0MatchesAction(
186 BytecodeNode* const node, const PeepholeActionAndData* action_data) {
187 DCHECK(LastIsValid());
188 DCHECK(!Bytecodes::IsJump(node->bytecode()));
189
190 if (last()->operand(0) == node->operand(0)) {
191 ElideCurrentAction(node);
192 } else {
193 DefaultAction(node);
194 }
195 }
196
ElideLastAction(BytecodeNode * const node,const PeepholeActionAndData * action_data)197 void BytecodePeepholeOptimizer::ElideLastAction(
198 BytecodeNode* const node, const PeepholeActionAndData* action_data) {
199 DCHECK(LastIsValid());
200 DCHECK(!Bytecodes::IsJump(node->bytecode()));
201
202 if (CanElideLastBasedOnSourcePosition(node)) {
203 if (last()->source_info().is_valid()) {
204 // |node| can not have a valid source position if the source
205 // position of last() is valid (per rules in
206 // CanElideLastBasedOnSourcePosition()).
207 node->set_source_info(last()->source_info());
208 }
209 SetLast(node);
210 } else {
211 DefaultAction(node);
212 }
213 }
214
ChangeBytecodeAction(BytecodeNode * const node,const PeepholeActionAndData * action_data)215 void BytecodePeepholeOptimizer::ChangeBytecodeAction(
216 BytecodeNode* const node, const PeepholeActionAndData* action_data) {
217 DCHECK(LastIsValid());
218 DCHECK(!Bytecodes::IsJump(node->bytecode()));
219
220 node->replace_bytecode(action_data->bytecode);
221 DefaultAction(node);
222 }
223
TransformLdaSmiBinaryOpToBinaryOpWithSmiAction(BytecodeNode * const node,const PeepholeActionAndData * action_data)224 void BytecodePeepholeOptimizer::TransformLdaSmiBinaryOpToBinaryOpWithSmiAction(
225 BytecodeNode* const node, const PeepholeActionAndData* action_data) {
226 DCHECK(LastIsValid());
227 DCHECK(!Bytecodes::IsJump(node->bytecode()));
228
229 if (!node->source_info().is_valid() || !last()->source_info().is_valid()) {
230 // Fused last and current into current.
231 TransformLdaSmiBinaryOpToBinaryOpWithSmi(action_data->bytecode, last(),
232 node);
233 SetLast(node);
234 } else {
235 DefaultAction(node);
236 }
237 }
238
239 void BytecodePeepholeOptimizer::
TransformLdaZeroBinaryOpToBinaryOpWithZeroAction(BytecodeNode * const node,const PeepholeActionAndData * action_data)240 TransformLdaZeroBinaryOpToBinaryOpWithZeroAction(
241 BytecodeNode* const node, const PeepholeActionAndData* action_data) {
242 DCHECK(LastIsValid());
243 DCHECK(!Bytecodes::IsJump(node->bytecode()));
244 if (!node->source_info().is_valid() || !last()->source_info().is_valid()) {
245 // Fused last and current into current.
246 TransformLdaZeroBinaryOpToBinaryOpWithZero(action_data->bytecode, last(),
247 node);
248 SetLast(node);
249 } else {
250 DefaultAction(node);
251 }
252 }
253
DefaultJumpAction(BytecodeNode * const node,const PeepholeActionAndData * action_data)254 void BytecodePeepholeOptimizer::DefaultJumpAction(
255 BytecodeNode* const node, const PeepholeActionAndData* action_data) {
256 DCHECK(LastIsValid());
257 DCHECK(Bytecodes::IsJump(node->bytecode()));
258
259 next_stage()->Write(last());
260 InvalidateLast();
261 }
262
UpdateLastJumpAction(BytecodeNode * const node,const PeepholeActionAndData * action_data)263 void BytecodePeepholeOptimizer::UpdateLastJumpAction(
264 BytecodeNode* const node, const PeepholeActionAndData* action_data) {
265 DCHECK(!LastIsValid());
266 DCHECK(Bytecodes::IsJump(node->bytecode()));
267 }
268
ChangeJumpBytecodeAction(BytecodeNode * const node,const PeepholeActionAndData * action_data)269 void BytecodePeepholeOptimizer::ChangeJumpBytecodeAction(
270 BytecodeNode* const node, const PeepholeActionAndData* action_data) {
271 DCHECK(LastIsValid());
272 DCHECK(Bytecodes::IsJump(node->bytecode()));
273
274 next_stage()->Write(last());
275 InvalidateLast();
276 node->set_bytecode(action_data->bytecode, node->operand(0));
277 }
278
ElideLastBeforeJumpAction(BytecodeNode * const node,const PeepholeActionAndData * action_data)279 void BytecodePeepholeOptimizer::ElideLastBeforeJumpAction(
280 BytecodeNode* const node, const PeepholeActionAndData* action_data) {
281 DCHECK(LastIsValid());
282 DCHECK(Bytecodes::IsJump(node->bytecode()));
283
284 if (!CanElideLastBasedOnSourcePosition(node)) {
285 next_stage()->Write(last());
286 } else if (!node->source_info().is_valid()) {
287 node->set_source_info(last()->source_info());
288 }
289 InvalidateLast();
290 }
291
ApplyPeepholeAction(BytecodeNode * const node)292 void BytecodePeepholeOptimizer::ApplyPeepholeAction(BytecodeNode* const node) {
293 // A single table is used for looking up peephole optimization
294 // matches as it is observed to have better performance. This is
295 // inspite of the fact that jump bytecodes and non-jump bytecodes
296 // have different processing logic, in particular a jump bytecode
297 // always needs to emit the jump via WriteJump().
298 const PeepholeActionAndData* const action_data =
299 PeepholeActionTable::Lookup(last()->bytecode(), node->bytecode());
300 switch (action_data->action) {
301 #define CASE(Action) \
302 case PeepholeAction::k##Action: \
303 Action(node, action_data); \
304 break;
305 PEEPHOLE_ACTION_LIST(CASE)
306 #undef CASE
307 default:
308 UNREACHABLE();
309 break;
310 }
311 }
312
313 } // namespace interpreter
314 } // namespace internal
315 } // namespace v8
316