OLD | NEW |
1 // Copyright 2016 the V8 project authors. All rights reserved. | 1 // Copyright 2015 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/interpreter/bytecode-peephole-optimizer.h" | 5 #include "src/interpreter/bytecode-peephole-optimizer.h" |
6 | 6 |
7 #include "src/interpreter/constant-array-builder.h" | 7 #include "src/interpreter/constant-array-builder.h" |
8 #include "src/objects-inl.h" | 8 #include "src/objects-inl.h" |
9 #include "src/objects.h" | 9 #include "src/objects.h" |
10 | 10 |
11 namespace v8 { | 11 namespace v8 { |
(...skipping 10 matching lines...) Expand all Loading... |
22 // override | 22 // override |
23 Handle<BytecodeArray> BytecodePeepholeOptimizer::ToBytecodeArray( | 23 Handle<BytecodeArray> BytecodePeepholeOptimizer::ToBytecodeArray( |
24 int fixed_register_count, int parameter_count, | 24 int fixed_register_count, int parameter_count, |
25 Handle<FixedArray> handler_table) { | 25 Handle<FixedArray> handler_table) { |
26 Flush(); | 26 Flush(); |
27 return next_stage_->ToBytecodeArray(fixed_register_count, parameter_count, | 27 return next_stage_->ToBytecodeArray(fixed_register_count, parameter_count, |
28 handler_table); | 28 handler_table); |
29 } | 29 } |
30 | 30 |
31 // override | 31 // override |
| 32 void BytecodePeepholeOptimizer::Write(BytecodeNode* node) { |
| 33 node = OptimizeAndEmitLast(node); |
| 34 if (node != nullptr) { |
| 35 SetLast(node); |
| 36 } |
| 37 } |
| 38 |
| 39 // override |
| 40 void BytecodePeepholeOptimizer::WriteJump(BytecodeNode* node, |
| 41 BytecodeLabel* label) { |
| 42 node = OptimizeAndEmitLast(node); |
| 43 next_stage_->WriteJump(node, label); |
| 44 } |
| 45 |
| 46 // override |
32 void BytecodePeepholeOptimizer::BindLabel(BytecodeLabel* label) { | 47 void BytecodePeepholeOptimizer::BindLabel(BytecodeLabel* label) { |
33 Flush(); | 48 Flush(); |
34 next_stage_->BindLabel(label); | 49 next_stage_->BindLabel(label); |
35 } | 50 } |
36 | 51 |
37 // override | 52 // override |
38 void BytecodePeepholeOptimizer::BindLabel(const BytecodeLabel& target, | 53 void BytecodePeepholeOptimizer::BindLabel(const BytecodeLabel& target, |
39 BytecodeLabel* label) { | 54 BytecodeLabel* label) { |
40 // There is no need to flush here, it will have been flushed when | 55 // There is no need to flush here, it will have been flushed when |target| |
41 // |target| was bound. | 56 // was bound. |
42 next_stage_->BindLabel(target, label); | 57 next_stage_->BindLabel(target, label); |
43 } | 58 } |
44 | 59 |
45 // override | |
46 void BytecodePeepholeOptimizer::WriteJump(BytecodeNode* node, | |
47 BytecodeLabel* label) { | |
48 // Handlers for jump bytecodes do not emit |node| as WriteJump() | |
49 // requires the |label| and having a label argument in all action | |
50 // handlers results in dead work in the non-jump case. | |
51 ApplyPeepholeAction(node); | |
52 next_stage()->WriteJump(node, label); | |
53 } | |
54 | |
55 // override | |
56 void BytecodePeepholeOptimizer::Write(BytecodeNode* node) { | |
57 // Handlers for non-jump bytecodes run to completion emitting | |
58 // bytecode to next stage as appropriate. | |
59 ApplyPeepholeAction(node); | |
60 } | |
61 | |
62 void BytecodePeepholeOptimizer::Flush() { | 60 void BytecodePeepholeOptimizer::Flush() { |
| 61 // TODO(oth/rmcilroy): We could check CanElideLast() here to potentially |
| 62 // eliminate last rather than writing it. |
63 if (LastIsValid()) { | 63 if (LastIsValid()) { |
64 next_stage_->Write(&last_); | 64 next_stage_->Write(&last_); |
65 InvalidateLast(); | 65 InvalidateLast(); |
66 } | 66 } |
67 } | 67 } |
68 | 68 |
69 void BytecodePeepholeOptimizer::InvalidateLast() { | 69 void BytecodePeepholeOptimizer::InvalidateLast() { |
70 last_.set_bytecode(Bytecode::kIllegal); | 70 last_.set_bytecode(Bytecode::kIllegal); |
71 } | 71 } |
72 | 72 |
73 bool BytecodePeepholeOptimizer::LastIsValid() const { | 73 bool BytecodePeepholeOptimizer::LastIsValid() const { |
74 return last_.bytecode() != Bytecode::kIllegal; | 74 return last_.bytecode() != Bytecode::kIllegal; |
75 } | 75 } |
76 | 76 |
77 void BytecodePeepholeOptimizer::SetLast(const BytecodeNode* const node) { | 77 void BytecodePeepholeOptimizer::SetLast(const BytecodeNode* const node) { |
78 // An action shouldn't leave a NOP as last bytecode unless it has | |
79 // source position information. NOP without source information can | |
80 // always be elided. | |
81 DCHECK(node->bytecode() != Bytecode::kNop || node->source_info().is_valid()); | |
82 | |
83 last_.Clone(node); | 78 last_.Clone(node); |
84 } | 79 } |
85 | 80 |
86 Handle<Object> BytecodePeepholeOptimizer::GetConstantForIndexOperand( | 81 Handle<Object> BytecodePeepholeOptimizer::GetConstantForIndexOperand( |
87 const BytecodeNode* const node, int index) const { | 82 const BytecodeNode* const node, int index) const { |
88 DCHECK_LE(index, node->operand_count()); | 83 DCHECK_LE(index, node->operand_count()); |
89 DCHECK_EQ(Bytecodes::GetOperandType(node->bytecode(), 0), OperandType::kIdx); | 84 DCHECK_EQ(Bytecodes::GetOperandType(node->bytecode(), 0), OperandType::kIdx); |
90 uint32_t index_operand = node->operand(0); | 85 uint32_t index_operand = node->operand(0); |
91 return constant_array_builder_->At(index_operand); | 86 return constant_array_builder_->At(index_operand); |
92 } | 87 } |
93 | 88 |
| 89 bool BytecodePeepholeOptimizer::LastBytecodePutsNameInAccumulator() const { |
| 90 DCHECK(LastIsValid()); |
| 91 return (last_.bytecode() == Bytecode::kTypeOf || |
| 92 last_.bytecode() == Bytecode::kToName || |
| 93 (last_.bytecode() == Bytecode::kLdaConstant && |
| 94 GetConstantForIndexOperand(&last_, 0)->IsName())); |
| 95 } |
| 96 |
| 97 void BytecodePeepholeOptimizer::TryToRemoveLastExpressionPosition( |
| 98 const BytecodeNode* const current) { |
| 99 if (current->source_info().is_valid() && |
| 100 last_.source_info().is_expression() && |
| 101 Bytecodes::IsWithoutExternalSideEffects(last_.bytecode())) { |
| 102 // The last bytecode has been marked as expression. It has no |
| 103 // external effects so can't throw and the current bytecode is a |
| 104 // source position. Remove the expression position on the last |
| 105 // bytecode to open up potential peephole optimizations and to |
| 106 // save the memory and perf cost of storing the unneeded |
| 107 // expression position. |
| 108 last_.source_info().set_invalid(); |
| 109 } |
| 110 } |
| 111 |
| 112 bool BytecodePeepholeOptimizer::CanElideCurrent( |
| 113 const BytecodeNode* const current) const { |
| 114 if (Bytecodes::IsLdarOrStar(last_.bytecode()) && |
| 115 Bytecodes::IsLdarOrStar(current->bytecode()) && |
| 116 current->operand(0) == last_.operand(0)) { |
| 117 // Ldar and Star make the accumulator and register hold equivalent |
| 118 // values. Only the first bytecode is needed if there's a sequence |
| 119 // of back-to-back Ldar and Star bytecodes with the same operand. |
| 120 return true; |
| 121 } else if (current->bytecode() == Bytecode::kToName && |
| 122 LastBytecodePutsNameInAccumulator()) { |
| 123 // If the previous bytecode ensured a name was in the accumulator, |
| 124 // the type coercion ToName() can be elided. |
| 125 return true; |
| 126 } else { |
| 127 // Additional candidates for eliding current: |
| 128 // (i) current is Nop. |
| 129 // (ii) ToNumber if the last puts a number in the accumulator. |
| 130 return false; |
| 131 } |
| 132 } |
| 133 |
94 bool BytecodePeepholeOptimizer::CanElideLastBasedOnSourcePosition( | 134 bool BytecodePeepholeOptimizer::CanElideLastBasedOnSourcePosition( |
95 const BytecodeNode* const current) const { | 135 const BytecodeNode* const current) const { |
96 // | 136 // |
97 // The rules for allowing the elision of the last bytecode based | 137 // The rules for allowing the elision of the last bytecode based |
98 // on source position are: | 138 // on source position are: |
99 // | 139 // |
100 // C U R R E N T | 140 // C U R R E N T |
101 // +--------+--------+--------+ | 141 // +--------+--------+--------+ |
102 // | None | Expr | Stmt | | 142 // | None | Expr | Stmt | |
103 // L +--------+--------+--------+--------+ | 143 // L +--------+--------+--------+--------+ |
104 // | None | YES | YES | YES | | 144 // | None | YES | YES | YES | |
105 // A +--------+--------+--------+--------+ | 145 // A +--------+--------+--------+--------+ |
106 // | Expr | YES | MAYBE | MAYBE | | 146 // | Expr | YES | MAYBE | MAYBE | |
107 // S +--------+--------+--------+--------+ | 147 // S +--------+--------+--------+--------+ |
108 // | Stmt | YES | NO | NO | | 148 // | Stmt | YES | NO | NO | |
109 // T +--------+--------+--------+--------+ | 149 // T +--------+--------+--------+--------+ |
110 // | 150 // |
111 // The goal is not lose any statement positions and not lose useful | 151 // The goal is not lose any statement positions and not lose useful |
112 // expression positions. Whenever the last bytecode is elided it's | 152 // expression positions. Whenever the last bytecode is elided it's |
113 // source position information is applied to the current node | 153 // source position information is applied to the current node |
114 // updating it if necessary. | 154 // updating it if necessary. |
115 // | 155 // |
116 // The last bytecode could be elided for the MAYBE cases if the last | 156 // The last bytecode can be elided for the MAYBE cases if the last |
117 // bytecode is known not to throw. If it throws, the system would | 157 // bytecode is known not to throw. If it throws, the system would |
118 // not have correct stack trace information. The appropriate check | 158 // not have correct stack trace information. The appropriate check |
119 // for this would be Bytecodes::IsWithoutExternalSideEffects(). By | 159 // for this would be Bytecodes::IsWithoutExternalSideEffects(), |
120 // default, the upstream bytecode generator filters out unneeded | 160 // which is checked in |
121 // expression position information so there is neglible benefit to | 161 // BytecodePeepholeOptimizer::TransformLastAndCurrentBytecodes() to |
122 // handling MAYBE specially. Hence MAYBE is treated the same as NO. | 162 // keep the check here simple. |
| 163 // |
| 164 // In rare cases, bytecode generation produces consecutive bytecodes |
| 165 // with the same expression positions. In principle, the latter of |
| 166 // these can be elided, but would make this function more expensive. |
123 // | 167 // |
124 return (!last_.source_info().is_valid() || | 168 return (!last_.source_info().is_valid() || |
125 !current->source_info().is_valid()); | 169 !current->source_info().is_valid()); |
126 } | 170 } |
127 | 171 |
128 namespace { | 172 namespace { |
129 | 173 |
130 void TransformLdaStarToLdrLdar(Bytecode new_bytecode, BytecodeNode* const last, | 174 void TransformLdaStarToLdrLdar(Bytecode new_bytecode, BytecodeNode* const last, |
131 BytecodeNode* const current) { | 175 BytecodeNode* const current) { |
132 DCHECK_EQ(current->bytecode(), Bytecode::kStar); | 176 DCHECK_EQ(current->bytecode(), Bytecode::kStar); |
133 | 177 |
134 // | 178 // |
135 // An example transformation here would be: | 179 // An example transformation here would be: |
136 // | 180 // |
137 // LdaGlobal i0, i1 ____\ LdrGlobal i0, i1, R | 181 // LdaGlobal i0, i1 ____\ LdrGlobal i0, i1, R |
138 // Star R ====/ Ldar R | 182 // Star R ====/ Ldar R |
139 // | 183 // |
140 // which loads a global value into both a register and the | 184 // which loads a global value into both a register and the |
141 // accumulator. However, in the second form the Ldar can often be | 185 // accumulator. However, in the second form the Ldar can often be |
142 // peephole optimized away unlike the Star in the first form. | 186 // peephole optimized away unlike the Star in the first form. |
143 // | 187 // |
144 last->Transform(new_bytecode, current->operand(0)); | 188 last->Transform(new_bytecode, current->operand(0)); |
145 current->set_bytecode(Bytecode::kLdar, current->operand(0)); | 189 current->set_bytecode(Bytecode::kLdar, current->operand(0)); |
146 } | 190 } |
147 | 191 |
148 void TransformLdaSmiBinaryOpToBinaryOpWithSmi(Bytecode new_bytecode, | 192 void TransformToBinaryOpWithSmiOnRhs(Bytecode new_bytecode, |
149 BytecodeNode* const last, | 193 BytecodeNode* const last, |
150 BytecodeNode* const current) { | 194 BytecodeNode* const current) { |
151 DCHECK_EQ(last->bytecode(), Bytecode::kLdaSmi); | 195 DCHECK(Bytecodes::IsLdaSmiOrLdaZero(last->bytecode())); |
152 current->set_bytecode(new_bytecode, last->operand(0), current->operand(0)); | 196 uint32_t imm_operand = |
153 if (last->source_info().is_valid()) { | 197 last->bytecode() == Bytecode::kLdaSmi ? last->operand(0) : 0; |
154 current->source_info().Clone(last->source_info()); | 198 current->set_bytecode(new_bytecode, imm_operand, current->operand(0)); |
155 } | |
156 } | |
157 | |
158 void TransformLdaZeroBinaryOpToBinaryOpWithZero(Bytecode new_bytecode, | |
159 BytecodeNode* const last, | |
160 BytecodeNode* const current) { | |
161 DCHECK_EQ(last->bytecode(), Bytecode::kLdaZero); | |
162 current->set_bytecode(new_bytecode, 0, current->operand(0)); | |
163 if (last->source_info().is_valid()) { | 199 if (last->source_info().is_valid()) { |
164 current->source_info().Clone(last->source_info()); | 200 current->source_info().Clone(last->source_info()); |
165 } | 201 } |
166 } | 202 } |
167 | 203 |
168 } // namespace | 204 } // namespace |
169 | 205 |
170 void BytecodePeepholeOptimizer::DefaultAction( | 206 bool BytecodePeepholeOptimizer::TransformLastAndCurrentBytecodes( |
171 BytecodeNode* const node, const PeepholeActionAndData* action_data) { | 207 BytecodeNode* const current) { |
172 DCHECK(LastIsValid()); | 208 if (current->bytecode() == Bytecode::kStar && |
173 DCHECK(!Bytecodes::IsJump(node->bytecode())); | 209 !current->source_info().is_statement()) { |
| 210 // Note: If the Star is tagged with a statement position, we can't |
| 211 // perform this transform as the store to the register will |
| 212 // have the wrong ordering for stepping in the debugger. |
| 213 switch (last_.bytecode()) { |
| 214 case Bytecode::kLdaNamedProperty: |
| 215 TransformLdaStarToLdrLdar(Bytecode::kLdrNamedProperty, &last_, current); |
| 216 return true; |
| 217 case Bytecode::kLdaKeyedProperty: |
| 218 TransformLdaStarToLdrLdar(Bytecode::kLdrKeyedProperty, &last_, current); |
| 219 return true; |
| 220 case Bytecode::kLdaGlobal: |
| 221 TransformLdaStarToLdrLdar(Bytecode::kLdrGlobal, &last_, current); |
| 222 return true; |
| 223 case Bytecode::kLdaContextSlot: |
| 224 TransformLdaStarToLdrLdar(Bytecode::kLdrContextSlot, &last_, current); |
| 225 return true; |
| 226 case Bytecode::kLdaUndefined: |
| 227 TransformLdaStarToLdrLdar(Bytecode::kLdrUndefined, &last_, current); |
| 228 return true; |
| 229 default: |
| 230 break; |
| 231 } |
| 232 } else if (Bytecodes::IsLdaSmiOrLdaZero(last_.bytecode()) && |
| 233 (!last_.source_info().is_valid() || |
| 234 !current->source_info().is_valid())) { |
| 235 switch (current->bytecode()) { |
| 236 case Bytecode::kAdd: |
| 237 TransformToBinaryOpWithSmiOnRhs(Bytecode::kAddSmi, &last_, current); |
| 238 InvalidateLast(); |
| 239 return true; |
| 240 case Bytecode::kSub: |
| 241 TransformToBinaryOpWithSmiOnRhs(Bytecode::kSubSmi, &last_, current); |
| 242 InvalidateLast(); |
| 243 return true; |
| 244 case Bytecode::kBitwiseOr: |
| 245 TransformToBinaryOpWithSmiOnRhs(Bytecode::kBitwiseOrSmi, &last_, |
| 246 current); |
| 247 InvalidateLast(); |
| 248 return true; |
| 249 case Bytecode::kBitwiseAnd: |
| 250 TransformToBinaryOpWithSmiOnRhs(Bytecode::kBitwiseAndSmi, &last_, |
| 251 current); |
| 252 InvalidateLast(); |
| 253 return true; |
| 254 case Bytecode::kShiftLeft: |
| 255 TransformToBinaryOpWithSmiOnRhs(Bytecode::kShiftLeftSmi, &last_, |
| 256 current); |
| 257 InvalidateLast(); |
| 258 return true; |
| 259 case Bytecode::kShiftRight: |
| 260 TransformToBinaryOpWithSmiOnRhs(Bytecode::kShiftRightSmi, &last_, |
| 261 current); |
| 262 InvalidateLast(); |
| 263 return true; |
| 264 default: |
| 265 break; |
| 266 } |
| 267 } |
174 | 268 |
175 next_stage()->Write(last()); | 269 return false; |
176 SetLast(node); | |
177 } | 270 } |
178 | 271 |
179 void BytecodePeepholeOptimizer::UpdateLastAction( | 272 bool BytecodePeepholeOptimizer::RemoveToBooleanFromJump( |
180 BytecodeNode* const node, const PeepholeActionAndData* action_data) { | 273 BytecodeNode* const current) { |
181 DCHECK(!LastIsValid()); | 274 bool can_remove = Bytecodes::IsJumpIfToBoolean(current->bytecode()) && |
182 DCHECK(!Bytecodes::IsJump(node->bytecode())); | 275 Bytecodes::WritesBooleanToAccumulator(last_.bytecode()); |
183 | 276 if (can_remove) { |
184 SetLast(node); | 277 // Conditional jumps with boolean conditions are emiitted in |
| 278 // ToBoolean form by the bytecode array builder, |
| 279 // i.e. JumpIfToBooleanTrue rather JumpIfTrue. The ToBoolean |
| 280 // element can be removed if the previous bytecode put a boolean |
| 281 // value in the accumulator. |
| 282 Bytecode jump = Bytecodes::GetJumpWithoutToBoolean(current->bytecode()); |
| 283 current->set_bytecode(jump, current->operand(0)); |
| 284 } |
| 285 return can_remove; |
185 } | 286 } |
186 | 287 |
187 void BytecodePeepholeOptimizer::UpdateLastIfSourceInfoPresentAction( | 288 bool BytecodePeepholeOptimizer::RemoveToBooleanFromLogicalNot( |
188 BytecodeNode* const node, const PeepholeActionAndData* action_data) { | 289 BytecodeNode* const current) { |
189 DCHECK(!LastIsValid()); | 290 bool can_remove = current->bytecode() == Bytecode::kToBooleanLogicalNot && |
190 DCHECK(!Bytecodes::IsJump(node->bytecode())); | 291 Bytecodes::WritesBooleanToAccumulator(last_.bytecode()); |
| 292 if (can_remove) { |
| 293 // Logical-nots are emitted in ToBoolean form by the bytecode array |
| 294 // builder, The ToBoolean element can be removed if the previous bytecode |
| 295 // put a boolean value in the accumulator. |
| 296 current->set_bytecode(Bytecode::kLogicalNot); |
| 297 } |
| 298 return can_remove; |
| 299 } |
191 | 300 |
192 if (node->source_info().is_valid()) { | 301 bool BytecodePeepholeOptimizer::TransformCurrentBytecode( |
193 SetLast(node); | 302 BytecodeNode* const current) { |
| 303 return RemoveToBooleanFromJump(current) || |
| 304 RemoveToBooleanFromLogicalNot(current); |
| 305 } |
| 306 |
| 307 bool BytecodePeepholeOptimizer::CanElideLast( |
| 308 const BytecodeNode* const current) const { |
| 309 if (last_.bytecode() == Bytecode::kNop) { |
| 310 // Nop are placeholders for holding source position information. |
| 311 return true; |
| 312 } else if (Bytecodes::IsAccumulatorLoadWithoutEffects(current->bytecode()) && |
| 313 Bytecodes::IsAccumulatorLoadWithoutEffects(last_.bytecode())) { |
| 314 // The accumulator is invisible to the debugger. If there is a sequence of |
| 315 // consecutive accumulator loads (that don't have side effects) then only |
| 316 // the final load is potentially visible. |
| 317 return true; |
| 318 } else if (Bytecodes::GetAccumulatorUse(current->bytecode()) == |
| 319 AccumulatorUse::kWrite && |
| 320 Bytecodes::IsAccumulatorLoadWithoutEffects(last_.bytecode())) { |
| 321 // The current instruction clobbers the accumulator without reading it. The |
| 322 // load in the last instruction can be elided as it has no effect. |
| 323 return true; |
| 324 } else { |
| 325 return false; |
194 } | 326 } |
195 } | 327 } |
196 | 328 |
197 void BytecodePeepholeOptimizer::ElideCurrentAction( | 329 BytecodeNode* BytecodePeepholeOptimizer::Optimize(BytecodeNode* current) { |
198 BytecodeNode* const node, const PeepholeActionAndData* action_data) { | 330 TryToRemoveLastExpressionPosition(current); |
199 DCHECK(LastIsValid()); | 331 if (TransformCurrentBytecode(current) || |
200 DCHECK(!Bytecodes::IsJump(node->bytecode())); | 332 TransformLastAndCurrentBytecodes(current)) { |
| 333 return current; |
| 334 } |
201 | 335 |
202 if (node->source_info().is_valid()) { | 336 if (CanElideCurrent(current)) { |
203 // Preserve the source information by replacing the node bytecode | 337 if (current->source_info().is_valid()) { |
204 // with a no op bytecode. | 338 // Preserve the source information by replacing the current bytecode |
205 node->set_bytecode(Bytecode::kNop); | 339 // with a no op bytecode. |
206 DefaultAction(node); | 340 current->set_bytecode(Bytecode::kNop); |
207 } else { | 341 } else { |
208 // Nothing to do, keep last and wait for next bytecode to pair with it. | 342 current = nullptr; |
| 343 } |
| 344 return current; |
209 } | 345 } |
| 346 |
| 347 if (CanElideLast(current) && CanElideLastBasedOnSourcePosition(current)) { |
| 348 if (last_.source_info().is_valid()) { |
| 349 // Current can not be valid per CanElideLastBasedOnSourcePosition(). |
| 350 current->source_info().Clone(last_.source_info()); |
| 351 } |
| 352 InvalidateLast(); |
| 353 return current; |
| 354 } |
| 355 |
| 356 return current; |
210 } | 357 } |
211 | 358 |
212 void BytecodePeepholeOptimizer::ElideCurrentIfOperand0MatchesAction( | 359 BytecodeNode* BytecodePeepholeOptimizer::OptimizeAndEmitLast( |
213 BytecodeNode* const node, const PeepholeActionAndData* action_data) { | 360 BytecodeNode* current) { |
214 DCHECK(LastIsValid()); | 361 // Attempt optimization if there is an earlier node to optimize with. |
215 DCHECK(!Bytecodes::IsJump(node->bytecode())); | 362 if (LastIsValid()) { |
216 | 363 current = Optimize(current); |
217 if (last()->operand(0) == node->operand(0)) { | 364 // Only output the last node if it wasn't invalidated by the optimization. |
218 ElideCurrentAction(node); | 365 if (LastIsValid()) { |
219 } else { | 366 next_stage_->Write(&last_); |
220 DefaultAction(node); | 367 InvalidateLast(); |
| 368 } |
221 } | 369 } |
222 } | 370 return current; |
223 | |
224 void BytecodePeepholeOptimizer::ElideCurrentIfLoadingNameConstantAction( | |
225 BytecodeNode* const node, const PeepholeActionAndData* action_data) { | |
226 DCHECK_EQ(last()->bytecode(), Bytecode::kLdaConstant); | |
227 DCHECK(!Bytecodes::IsJump(node->bytecode())); | |
228 | |
229 if (GetConstantForIndexOperand(last(), 0)->IsName()) { | |
230 ElideCurrentAction(node); | |
231 } else { | |
232 DefaultAction(node); | |
233 } | |
234 } | |
235 | |
236 void BytecodePeepholeOptimizer::ElideLastAction( | |
237 BytecodeNode* const node, const PeepholeActionAndData* action_data) { | |
238 DCHECK(LastIsValid()); | |
239 DCHECK(!Bytecodes::IsJump(node->bytecode())); | |
240 | |
241 if (CanElideLastBasedOnSourcePosition(node)) { | |
242 if (last()->source_info().is_valid()) { | |
243 // |node| can not have a valid source position if the source | |
244 // position of last() is valid (per rules in | |
245 // CanElideLastBasedOnSourcePosition()). | |
246 node->source_info().Clone(last()->source_info()); | |
247 } | |
248 SetLast(node); | |
249 } else { | |
250 DefaultAction(node); | |
251 } | |
252 } | |
253 | |
254 void BytecodePeepholeOptimizer::ChangeBytecodeAction( | |
255 BytecodeNode* const node, const PeepholeActionAndData* action_data) { | |
256 DCHECK(LastIsValid()); | |
257 DCHECK(!Bytecodes::IsJump(node->bytecode())); | |
258 | |
259 node->set_bytecode(action_data->bytecode); | |
260 DefaultAction(node); | |
261 } | |
262 | |
263 void BytecodePeepholeOptimizer::TransformLdaStarToLdrLdarAction( | |
264 BytecodeNode* const node, const PeepholeActionAndData* action_data) { | |
265 DCHECK(LastIsValid()); | |
266 DCHECK(!Bytecodes::IsJump(node->bytecode())); | |
267 | |
268 if (!node->source_info().is_statement()) { | |
269 TransformLdaStarToLdrLdar(action_data->bytecode, last(), node); | |
270 } | |
271 DefaultAction(node); | |
272 } | |
273 | |
274 void BytecodePeepholeOptimizer::TransformLdaSmiBinaryOpToBinaryOpWithSmiAction( | |
275 BytecodeNode* const node, const PeepholeActionAndData* action_data) { | |
276 DCHECK(LastIsValid()); | |
277 DCHECK(!Bytecodes::IsJump(node->bytecode())); | |
278 | |
279 if (!node->source_info().is_valid() || !last()->source_info().is_valid()) { | |
280 // Fused last and current into current. | |
281 TransformLdaSmiBinaryOpToBinaryOpWithSmi(action_data->bytecode, last(), | |
282 node); | |
283 SetLast(node); | |
284 } else { | |
285 DefaultAction(node); | |
286 } | |
287 } | |
288 | |
289 void BytecodePeepholeOptimizer:: | |
290 TransformLdaZeroBinaryOpToBinaryOpWithZeroAction( | |
291 BytecodeNode* const node, const PeepholeActionAndData* action_data) { | |
292 DCHECK(LastIsValid()); | |
293 DCHECK(!Bytecodes::IsJump(node->bytecode())); | |
294 if (!node->source_info().is_valid() || !last()->source_info().is_valid()) { | |
295 // Fused last and current into current. | |
296 TransformLdaZeroBinaryOpToBinaryOpWithZero(action_data->bytecode, last(), | |
297 node); | |
298 SetLast(node); | |
299 } else { | |
300 DefaultAction(node); | |
301 } | |
302 } | |
303 | |
304 void BytecodePeepholeOptimizer::DefaultJumpAction( | |
305 BytecodeNode* const node, const PeepholeActionAndData* action_data) { | |
306 DCHECK(LastIsValid()); | |
307 DCHECK(Bytecodes::IsJump(node->bytecode())); | |
308 | |
309 next_stage()->Write(last()); | |
310 InvalidateLast(); | |
311 } | |
312 | |
313 void BytecodePeepholeOptimizer::UpdateLastJumpAction( | |
314 BytecodeNode* const node, const PeepholeActionAndData* action_data) { | |
315 DCHECK(!LastIsValid()); | |
316 DCHECK(Bytecodes::IsJump(node->bytecode())); | |
317 } | |
318 | |
319 void BytecodePeepholeOptimizer::ChangeJumpBytecodeAction( | |
320 BytecodeNode* const node, const PeepholeActionAndData* action_data) { | |
321 DCHECK(LastIsValid()); | |
322 DCHECK(Bytecodes::IsJump(node->bytecode())); | |
323 | |
324 next_stage()->Write(last()); | |
325 InvalidateLast(); | |
326 node->set_bytecode(action_data->bytecode, node->operand(0)); | |
327 } | |
328 | |
329 void BytecodePeepholeOptimizer::ElideLastBeforeJumpAction( | |
330 BytecodeNode* const node, const PeepholeActionAndData* action_data) { | |
331 DCHECK(LastIsValid()); | |
332 DCHECK(Bytecodes::IsJump(node->bytecode())); | |
333 DCHECK(CanElideLastBasedOnSourcePosition(node)); | |
334 | |
335 if (!node->source_info().is_valid()) { | |
336 node->source_info().Clone(last()->source_info()); | |
337 } else { | |
338 next_stage()->Write(last()); | |
339 } | |
340 InvalidateLast(); | |
341 } | |
342 | |
343 void BytecodePeepholeOptimizer::ApplyPeepholeAction(BytecodeNode* const node) { | |
344 // A single table is used for looking up peephole optimization | |
345 // matches as it is observed to have better performance. This is | |
346 // inspite of the fact that jump bytecodes and non-jump bytecodes | |
347 // have different processing logic, in particular a jump bytecode | |
348 // always needs to emit the jump via WriteJump(). | |
349 const PeepholeActionAndData* const action_data = | |
350 PeepholeActionTable::Lookup(last()->bytecode(), node->bytecode()); | |
351 switch (action_data->action) { | |
352 #define CASE(Action) \ | |
353 case PeepholeAction::k##Action: \ | |
354 Action(node, action_data); \ | |
355 break; | |
356 PEEPHOLE_ACTION_LIST(CASE) | |
357 #undef CASE | |
358 default: | |
359 UNREACHABLE(); | |
360 break; | |
361 } | |
362 } | 371 } |
363 | 372 |
364 } // namespace interpreter | 373 } // namespace interpreter |
365 } // namespace internal | 374 } // namespace internal |
366 } // namespace v8 | 375 } // namespace v8 |
OLD | NEW |