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