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