OLD | NEW |
1 // Copyright 2015 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 |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
87 } | 87 } |
88 | 88 |
89 bool BytecodePeepholeOptimizer::LastBytecodePutsNameInAccumulator() const { | 89 bool BytecodePeepholeOptimizer::LastBytecodePutsNameInAccumulator() const { |
90 DCHECK(LastIsValid()); | 90 DCHECK(LastIsValid()); |
91 return (last_.bytecode() == Bytecode::kTypeOf || | 91 return (last_.bytecode() == Bytecode::kTypeOf || |
92 last_.bytecode() == Bytecode::kToName || | 92 last_.bytecode() == Bytecode::kToName || |
93 (last_.bytecode() == Bytecode::kLdaConstant && | 93 (last_.bytecode() == Bytecode::kLdaConstant && |
94 GetConstantForIndexOperand(&last_, 0)->IsName())); | 94 GetConstantForIndexOperand(&last_, 0)->IsName())); |
95 } | 95 } |
96 | 96 |
97 void BytecodePeepholeOptimizer::UpdateLastAndCurrentBytecodes( | 97 void BytecodePeepholeOptimizer::TryToRemoveLastExpressionPosition( |
98 BytecodeNode* current) { | 98 const BytecodeNode* const current) { |
99 if (Bytecodes::IsJumpIfToBoolean(current->bytecode()) && | |
100 Bytecodes::WritesBooleanToAccumulator(last_.bytecode())) { | |
101 // Conditional jumps with boolean conditions are emitted in | |
102 // ToBoolean form by the bytecode array builder, | |
103 // i.e. JumpIfToBooleanTrue rather JumpIfTrue. The ToBoolean element | |
104 // can be removed if the previous bytecode put a boolean value in | |
105 // the accumulator. | |
106 Bytecode jump = Bytecodes::GetJumpWithoutToBoolean(current->bytecode()); | |
107 current->set_bytecode(jump, current->operand(0), current->operand_scale()); | |
108 } else if (current->bytecode() == Bytecode::kToBooleanLogicalNot && | |
109 Bytecodes::WritesBooleanToAccumulator(last_.bytecode())) { | |
110 // Logical-nots are emitted in ToBoolean form by the bytecode array | |
111 // builder, The ToBoolean element can be removed if the previous bytecode | |
112 // put a boolean value in the accumulator. | |
113 current->set_bytecode(Bytecode::kLogicalNot); | |
114 } | |
115 | |
116 if (current->source_info().is_statement() && | 99 if (current->source_info().is_statement() && |
117 last_.source_info().is_expression() && | 100 last_.source_info().is_expression() && |
118 Bytecodes::IsWithoutExternalSideEffects(last_.bytecode())) { | 101 Bytecodes::IsWithoutExternalSideEffects(last_.bytecode())) { |
119 // The last bytecode has been marked as expression. It has no | 102 // The last bytecode has been marked as expression. It has no |
120 // external effects so can't throw and the current bytecode is a | 103 // external effects so can't throw and the current bytecode is a |
121 // source position. Remove the expression position on the last | 104 // source position. Remove the expression position on the last |
122 // bytecode to open up potential peephole optimizations and to | 105 // bytecode to open up potential peephole optimizations and to |
123 // save the memory and perf cost of storing the unneeded | 106 // save the memory and perf cost of storing the unneeded |
124 // expression position. | 107 // expression position. |
125 last_.source_info().set_invalid(); | 108 last_.source_info().set_invalid(); |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
162 // | Expr | YES | MAYBE | MAYBE | | 145 // | Expr | YES | MAYBE | MAYBE | |
163 // S +--------+--------+--------+--------+ | 146 // S +--------+--------+--------+--------+ |
164 // | Stmt | YES | NO | NO | | 147 // | Stmt | YES | NO | NO | |
165 // T +--------+--------+--------+--------+ | 148 // T +--------+--------+--------+--------+ |
166 // | 149 // |
167 // The goal is not lose any statement positions and not lose useful | 150 // The goal is not lose any statement positions and not lose useful |
168 // expression positions. Whenever the last bytecode is elided it's | 151 // expression positions. Whenever the last bytecode is elided it's |
169 // source position information is applied to the current node | 152 // source position information is applied to the current node |
170 // updating it if necessary. | 153 // updating it if necessary. |
171 // | 154 // |
172 // | |
173 // The last bytecode can be elided for the MAYBE cases if the last | 155 // The last bytecode can be elided for the MAYBE cases if the last |
174 // bytecode is known not to throw. If it throws, the system would | 156 // bytecode is known not to throw. If it throws, the system would |
175 // not have correct stack trace information. The appropriate check | 157 // not have correct stack trace information. The appropriate check |
176 // for this would be Bytecodes::IsWithoutExternalSideEffects(), which | 158 // for this would be Bytecodes::IsWithoutExternalSideEffects(), |
177 // is checked in BytecodePeepholeOptimizer::UpdateLastAndCurrentBytecodes() | 159 // which is checked in |
178 // to keep the check here simple. | 160 // BytecodePeepholeOptimizer::TransformLastAndCurrentBytecodes() to |
| 161 // keep the check here simple. |
179 // | 162 // |
180 // In rare cases, bytecode generation produces consecutive bytecodes | 163 // In rare cases, bytecode generation produces consecutive bytecodes |
181 // with the same expression positions. In principle, the latter of | 164 // with the same expression positions. In principle, the latter of |
182 // these can be elided, but would make this function more expensive. | 165 // these can be elided, but would make this function more expensive. |
183 // | 166 // |
184 return (!last_.source_info().is_valid() || | 167 return (!last_.source_info().is_valid() || |
185 !current->source_info().is_valid()); | 168 !current->source_info().is_valid()); |
186 } | 169 } |
187 | 170 |
| 171 namespace { |
| 172 |
| 173 void TransformLdaStarToLdrLdar(Bytecode new_bytecode, BytecodeNode* const last, |
| 174 BytecodeNode* const current) { |
| 175 DCHECK_EQ(current->bytecode(), Bytecode::kStar); |
| 176 // |
| 177 // An example transformation here would be: |
| 178 // |
| 179 // LdaGlobal i0, i1 ____\ LdrGlobal i0, i1, R |
| 180 // Star R ====/ Ldar R |
| 181 // |
| 182 // which loads a global value into both a register and the |
| 183 // accumulator. However, in the second form the Ldar can often be |
| 184 // peephole optimized away unlike the Star in the first form. |
| 185 // |
| 186 last->Transform(new_bytecode, current->operand(0), current->operand_scale()); |
| 187 current->set_bytecode(Bytecode::kLdar, current->operand(0), |
| 188 current->operand_scale()); |
| 189 |
| 190 // If there was a source position on |current| transfer it to the |
| 191 // updated |last| to maintain the debugger's causal view. ie. if an |
| 192 // expression position LdrGlobal is the bytecode that could throw |
| 193 // and if a statement position it needs to be placed before the |
| 194 // store to R occurs. |
| 195 last->source_info().Update(current->source_info()); |
| 196 current->source_info().set_invalid(); |
| 197 } |
| 198 |
| 199 } // namespace |
| 200 |
| 201 bool BytecodePeepholeOptimizer::ChangeLdaToLdr(BytecodeNode* const current) { |
| 202 if (current->bytecode() == Bytecode::kStar) { |
| 203 switch (last_.bytecode()) { |
| 204 case Bytecode::kLoadIC: |
| 205 TransformLdaStarToLdrLdar(Bytecode::kLdrNamedProperty, &last_, current); |
| 206 return true; |
| 207 case Bytecode::kKeyedLoadIC: |
| 208 TransformLdaStarToLdrLdar(Bytecode::kLdrKeyedProperty, &last_, current); |
| 209 return true; |
| 210 case Bytecode::kLdaGlobal: |
| 211 TransformLdaStarToLdrLdar(Bytecode::kLdrGlobal, &last_, current); |
| 212 return true; |
| 213 case Bytecode::kLdaContextSlot: |
| 214 TransformLdaStarToLdrLdar(Bytecode::kLdrContextSlot, &last_, current); |
| 215 return true; |
| 216 case Bytecode::kLdaUndefined: |
| 217 TransformLdaStarToLdrLdar(Bytecode::kLdrUndefined, &last_, current); |
| 218 return true; |
| 219 default: |
| 220 break; |
| 221 } |
| 222 } |
| 223 return false; |
| 224 } |
| 225 |
| 226 bool BytecodePeepholeOptimizer::RemoveToBooleanFromJump( |
| 227 BytecodeNode* const current) { |
| 228 bool can_remove = Bytecodes::IsJumpIfToBoolean(current->bytecode()) && |
| 229 Bytecodes::WritesBooleanToAccumulator(last_.bytecode()); |
| 230 if (can_remove) { |
| 231 // Conditional jumps with boolean conditions are emiitted in |
| 232 // ToBoolean form by the bytecode array builder, |
| 233 // i.e. JumpIfToBooleanTrue rather JumpIfTrue. The ToBoolean |
| 234 // element can be removed if the previous bytecode put a boolean |
| 235 // value in the accumulator. |
| 236 Bytecode jump = Bytecodes::GetJumpWithoutToBoolean(current->bytecode()); |
| 237 current->set_bytecode(jump, current->operand(0), current->operand_scale()); |
| 238 } |
| 239 return can_remove; |
| 240 } |
| 241 |
| 242 bool BytecodePeepholeOptimizer::RemoveToBooleanFromLogicalNot( |
| 243 BytecodeNode* const current) { |
| 244 bool can_remove = current->bytecode() == Bytecode::kToBooleanLogicalNot && |
| 245 Bytecodes::WritesBooleanToAccumulator(last_.bytecode()); |
| 246 if (can_remove) { |
| 247 // Logical-nots are emitted in ToBoolean form by the bytecode array |
| 248 // builder, The ToBoolean element can be removed if the previous bytecode |
| 249 // put a boolean value in the accumulator. |
| 250 current->set_bytecode(Bytecode::kLogicalNot); |
| 251 } |
| 252 return can_remove; |
| 253 } |
| 254 |
| 255 bool BytecodePeepholeOptimizer::TransformLastAndCurrentBytecodes( |
| 256 BytecodeNode* const current) { |
| 257 return RemoveToBooleanFromJump(current) || |
| 258 RemoveToBooleanFromLogicalNot(current) || ChangeLdaToLdr(current); |
| 259 } |
| 260 |
188 bool BytecodePeepholeOptimizer::CanElideLast( | 261 bool BytecodePeepholeOptimizer::CanElideLast( |
189 const BytecodeNode* const current) const { | 262 const BytecodeNode* const current) const { |
190 if (!last_is_discardable_) { | 263 if (!last_is_discardable_) { |
191 return false; | 264 return false; |
192 } | 265 } |
193 | 266 |
194 if (last_.bytecode() == Bytecode::kNop) { | 267 if (last_.bytecode() == Bytecode::kNop) { |
195 // Nop are placeholders for holding source position information. | 268 // Nop are placeholders for holding source position information. |
196 return true; | 269 return true; |
197 } else if (Bytecodes::IsAccumulatorLoadWithoutEffects(current->bytecode()) && | 270 } else if (Bytecodes::IsAccumulatorLoadWithoutEffects(current->bytecode()) && |
198 Bytecodes::IsAccumulatorLoadWithoutEffects(last_.bytecode())) { | 271 Bytecodes::IsAccumulatorLoadWithoutEffects(last_.bytecode())) { |
199 // The accumulator is invisible to the debugger. If there is a sequence of | 272 // The accumulator is invisible to the debugger. If there is a sequence of |
200 // consecutive accumulator loads (that don't have side effects) then only | 273 // consecutive accumulator loads (that don't have side effects) then only |
201 // the final load is potentially visible. | 274 // the final load is potentially visible. |
202 return true; | 275 return true; |
| 276 } else if (Bytecodes::GetAccumulatorUse(current->bytecode()) == |
| 277 AccumulatorUse::kWrite && |
| 278 Bytecodes::IsAccumulatorLoadWithoutEffects(last_.bytecode())) { |
| 279 // The current instruction clobbers the accumulator without reading it. The |
| 280 // load in the last instruction can be elided as it has no effect. |
| 281 return true; |
203 } else { | 282 } else { |
204 return false; | 283 return false; |
205 } | 284 } |
206 } | 285 } |
207 | 286 |
208 BytecodeNode* BytecodePeepholeOptimizer::Optimize(BytecodeNode* current) { | 287 BytecodeNode* BytecodePeepholeOptimizer::Optimize(BytecodeNode* current) { |
209 UpdateLastAndCurrentBytecodes(current); | 288 TryToRemoveLastExpressionPosition(current); |
| 289 |
| 290 if (TransformLastAndCurrentBytecodes(current)) { |
| 291 return current; |
| 292 } |
| 293 |
210 if (CanElideCurrent(current)) { | 294 if (CanElideCurrent(current)) { |
211 if (current->source_info().is_valid()) { | 295 if (current->source_info().is_valid()) { |
| 296 // Preserve the source information by replacing the current bytecode |
| 297 // with a no op bytecode. |
212 current->set_bytecode(Bytecode::kNop); | 298 current->set_bytecode(Bytecode::kNop); |
213 } else { | 299 } else { |
214 current = nullptr; | 300 current = nullptr; |
215 } | 301 } |
216 } else if (CanElideLast(current) && | 302 return current; |
217 CanElideLastBasedOnSourcePosition(current)) { | 303 } |
| 304 |
| 305 if (CanElideLast(current) && CanElideLastBasedOnSourcePosition(current)) { |
218 current->source_info().Update(last_.source_info()); | 306 current->source_info().Update(last_.source_info()); |
219 InvalidateLast(); | 307 InvalidateLast(); |
| 308 return current; |
220 } | 309 } |
| 310 |
221 return current; | 311 return current; |
222 } | 312 } |
223 | 313 |
224 } // namespace interpreter | 314 } // namespace interpreter |
225 } // namespace internal | 315 } // namespace internal |
226 } // namespace v8 | 316 } // namespace v8 |
OLD | NEW |