Chromium Code Reviews| 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 |
| 11 namespace v8 { | 11 namespace v8 { |
| 12 namespace internal { | 12 namespace internal { |
| 13 namespace interpreter { | 13 namespace interpreter { |
| 14 | 14 |
| 15 namespace { | |
| 16 | |
| 17 // Actions to take when a pair of bytes is encountered. A handler | |
| 18 // exists for each action. | |
| 19 enum class PeepholeAction : uint8_t { | |
| 20 // Actions for non-jump bytecodes. | |
| 21 kDefaultAction, | |
| 22 kUpdateLastAction, | |
| 23 kElideCurrentAction, | |
| 24 kElideCurrentIfOperand0MatchesAction, | |
| 25 kElideCurrentIfLoadingNameConstantAction, | |
| 26 kElideLastAction, | |
| 27 kChangeBytecodeAction, | |
| 28 kTransformLdaStarToLdrLdarAction, | |
| 29 | |
| 30 // Actions for jump bytecodes. | |
| 31 kDefaultJumpAction, | |
| 32 kUpdateLastJumpAction, | |
| 33 kChangeJumpBytecodeAction, | |
| 34 kElideLastBeforeJumpAction, | |
| 35 }; | |
| 36 | |
| 37 // Tuple of action to take when pair of bytecodes is encountered and | |
| 38 // optional data to invoke handler with. | |
| 39 struct PeepholeActionAndData final { | |
| 40 // Action to take when tuple of bytecodes encountered. | |
| 41 PeepholeAction action; | |
| 42 | |
| 43 // Replacement bytecode (if valid). | |
| 44 Bytecode bytecode; | |
| 45 }; | |
| 46 | |
| 47 class PeepholeActionTable final { | |
| 48 public: | |
| 49 static void Initialize(); | |
| 50 | |
| 51 static const PeepholeActionAndData* const Lookup(Bytecode last, | |
| 52 Bytecode current) { | |
| 53 size_t index = ToIndex(last, current); | |
| 54 return &table_[index]; | |
| 55 } | |
| 56 | |
| 57 static bool initialized() { return initialized_; } | |
| 58 | |
| 59 private: | |
| 60 static const size_t kNumberOfBytecodes = | |
| 61 static_cast<size_t>(Bytecode::kLast) + 1; | |
| 62 static const size_t kTableSize = kNumberOfBytecodes * kNumberOfBytecodes; | |
| 63 | |
| 64 static PeepholeActionAndData LookupActionAndData(Bytecode last, | |
| 65 Bytecode current); | |
| 66 static void InsertActionAndData(Bytecode last, Bytecode current, | |
| 67 PeepholeActionAndData action_data); | |
| 68 static Bytecode NextBytecode(Bytecode bytecode); | |
| 69 static size_t ToIndex(Bytecode last, Bytecode current); | |
| 70 | |
| 71 static bool initialized_; | |
| 72 static PeepholeActionAndData table_[kTableSize]; | |
| 73 }; | |
| 74 | |
| 75 bool PeepholeActionTable::initialized_ = false; | |
| 76 PeepholeActionAndData PeepholeActionTable::table_[kTableSize]; | |
| 77 | |
| 78 // static | |
| 79 void PeepholeActionTable::Initialize() { | |
| 80 DCHECK(!initialized_); | |
| 81 for (Bytecode last = Bytecode::kWide; last <= Bytecode::kLast; | |
| 82 last = NextBytecode(last)) { | |
| 83 for (Bytecode current = Bytecode::kWide; current <= Bytecode::kLast; | |
| 84 current = NextBytecode(current)) { | |
| 85 PeepholeActionAndData action_data = LookupActionAndData(last, current); | |
| 86 InsertActionAndData(last, current, action_data); | |
| 87 } | |
| 88 } | |
| 89 initialized_ = true; | |
| 90 } | |
| 91 | |
| 92 // static | |
| 93 Bytecode PeepholeActionTable::NextBytecode(Bytecode bytecode) { | |
| 94 return static_cast<Bytecode>(static_cast<size_t>(bytecode) + 1); | |
| 95 } | |
| 96 | |
| 97 // static | |
| 98 size_t PeepholeActionTable::ToIndex(Bytecode last, Bytecode current) { | |
| 99 return static_cast<size_t>(last) * kNumberOfBytecodes + | |
| 100 static_cast<size_t>(current); | |
| 101 } | |
| 102 | |
| 103 // static | |
| 104 void PeepholeActionTable::InsertActionAndData( | |
| 105 Bytecode last, Bytecode current, PeepholeActionAndData action_data) { | |
| 106 size_t index = ToIndex(last, current); | |
| 107 table_[index] = action_data; | |
| 108 } | |
| 109 | |
| 110 // static | |
| 111 PeepholeActionAndData PeepholeActionTable::LookupActionAndData( | |
| 112 Bytecode last, Bytecode current) { | |
| 113 // TODO(oth): Investigate generating this table at compile time. | |
| 114 | |
| 115 // Optimize various accumulator loads followed by store accumulator | |
| 116 // to an equivalent register load and loading the accumulator with | |
| 117 // the register. The latter accumulator load can often be elided as | |
| 118 // it is side-effect free and often followed by another accumulator | |
| 119 // load so can be elided. | |
| 120 if (current == Bytecode::kStar && last == Bytecode::kLdaNamedProperty) { | |
| 121 return {PeepholeAction::kTransformLdaStarToLdrLdarAction, | |
| 122 Bytecode::kLdrNamedProperty}; | |
| 123 } else if (current == Bytecode::kStar && | |
| 124 last == Bytecode::kLdaKeyedProperty) { | |
| 125 return {PeepholeAction::kTransformLdaStarToLdrLdarAction, | |
| 126 Bytecode::kLdrKeyedProperty}; | |
| 127 } else if (current == Bytecode::kStar && last == Bytecode::kLdaGlobal) { | |
| 128 return {PeepholeAction::kTransformLdaStarToLdrLdarAction, | |
| 129 Bytecode::kLdrGlobal}; | |
| 130 } else if (current == Bytecode::kStar && last == Bytecode::kLdaContextSlot) { | |
| 131 return {PeepholeAction::kTransformLdaStarToLdrLdarAction, | |
| 132 Bytecode::kLdrContextSlot}; | |
| 133 } else if (current == Bytecode::kStar && last == Bytecode::kLdaUndefined) { | |
| 134 return {PeepholeAction::kTransformLdaStarToLdrLdarAction, | |
| 135 Bytecode::kLdrUndefined}; | |
| 136 } | |
| 137 | |
| 138 // ToName optimizations: remove unnecessary ToName bytecodes. | |
| 139 if (last == Bytecode::kLdaConstant && current == Bytecode::kToName) { | |
| 140 return {PeepholeAction::kElideCurrentIfLoadingNameConstantAction, | |
| 141 Bytecode::kIllegal}; | |
| 142 } else if (Bytecodes::PutsNameInAccumulator(last) && | |
| 143 current == Bytecode::kToName) { | |
| 144 return {PeepholeAction::kElideCurrentAction, Bytecode::kIllegal}; | |
| 145 } | |
| 146 | |
| 147 // Nop are placeholders for holding source position information and can be | |
| 148 // elided if there is no source information. | |
| 149 if (last == Bytecode::kNop) { | |
| 150 if (Bytecodes::IsJump(current)) { | |
| 151 return {PeepholeAction::kElideLastBeforeJumpAction, Bytecode::kIllegal}; | |
| 152 } else { | |
| 153 return {PeepholeAction::kElideLastAction, Bytecode::kIllegal}; | |
| 154 } | |
| 155 } | |
| 156 | |
| 157 // The accumulator is invisible to the debugger. If there is a sequence | |
| 158 // of consecutive accumulator loads (that don't have side effects) then | |
| 159 // only the final load is potentially visible. | |
| 160 if (Bytecodes::IsAccumulatorLoadWithoutEffects(last) && | |
| 161 Bytecodes::IsAccumulatorLoadWithoutEffects(current)) { | |
| 162 return {PeepholeAction::kElideLastAction, Bytecode::kIllegal}; | |
| 163 } | |
| 164 | |
| 165 // The current instruction clobbers the accumulator without reading | |
| 166 // it. The load in the last instruction can be elided as it has no | |
| 167 // effect. | |
| 168 if (Bytecodes::IsAccumulatorLoadWithoutEffects(last) && | |
| 169 Bytecodes::GetAccumulatorUse(current) == AccumulatorUse::kWrite) { | |
| 170 return {PeepholeAction::kElideLastAction, Bytecode::kIllegal}; | |
| 171 } | |
| 172 | |
| 173 // Ldar and Star make the accumulator and register hold equivalent | |
| 174 // values. Only the first bytecode is needed if there's a sequence | |
| 175 // of back-to-back Ldar and Star bytecodes with the same operand. | |
| 176 if (Bytecodes::IsLdarOrStar(last) && Bytecodes::IsLdarOrStar(current)) { | |
| 177 return {PeepholeAction::kElideCurrentIfOperand0MatchesAction, | |
| 178 Bytecode::kIllegal}; | |
| 179 } | |
| 180 | |
| 181 // Remove ToBoolean coercion from conditional jumps where possible. | |
| 182 if (Bytecodes::WritesBooleanToAccumulator(last) && | |
| 183 Bytecodes::IsJumpIfToBoolean(current)) { | |
| 184 return {PeepholeAction::kChangeJumpBytecodeAction, | |
| 185 Bytecodes::GetJumpWithoutToBoolean(current)}; | |
| 186 } else if (Bytecodes::WritesBooleanToAccumulator(last) && | |
| 187 current == Bytecode::kToBooleanLogicalNot) { | |
| 188 return {PeepholeAction::kChangeBytecodeAction, Bytecode::kLogicalNot}; | |
| 189 } | |
| 190 | |
| 191 // If there is no last bytecode to optimize against, store the incoming | |
| 192 // bytecode or for jumps emit incoming bytecode immediately. | |
| 193 if (last == Bytecode::kIllegal) { | |
| 194 if (Bytecodes::IsJump(current)) { | |
| 195 return {PeepholeAction::kUpdateLastJumpAction, Bytecode::kIllegal}; | |
| 196 } else { | |
| 197 return {PeepholeAction::kUpdateLastAction, Bytecode::kIllegal}; | |
| 198 } | |
| 199 } | |
| 200 | |
| 201 // No matches, take the default action. | |
| 202 if (Bytecodes::IsJump(current)) { | |
| 203 return {PeepholeAction::kDefaultJumpAction, Bytecode::kIllegal}; | |
| 204 } else { | |
| 205 return {PeepholeAction::kDefaultAction, Bytecode::kIllegal}; | |
| 206 } | |
| 207 } | |
| 208 | |
| 209 } // namespace | |
| 210 | |
| 15 BytecodePeepholeOptimizer::BytecodePeepholeOptimizer( | 211 BytecodePeepholeOptimizer::BytecodePeepholeOptimizer( |
| 16 ConstantArrayBuilder* constant_array_builder, | 212 ConstantArrayBuilder* constant_array_builder, |
| 17 BytecodePipelineStage* next_stage) | 213 BytecodePipelineStage* next_stage) |
| 18 : constant_array_builder_(constant_array_builder), next_stage_(next_stage) { | 214 : constant_array_builder_(constant_array_builder), next_stage_(next_stage) { |
| 215 if (!PeepholeActionTable::initialized()) { | |
| 216 PeepholeActionTable::Initialize(); | |
|
rmcilroy
2016/07/04 12:45:46
How does this play with threading? There could be
| |
| 217 } | |
| 19 InvalidateLast(); | 218 InvalidateLast(); |
| 20 } | 219 } |
| 21 | 220 |
| 22 // override | 221 // override |
| 23 Handle<BytecodeArray> BytecodePeepholeOptimizer::ToBytecodeArray( | 222 Handle<BytecodeArray> BytecodePeepholeOptimizer::ToBytecodeArray( |
| 24 int fixed_register_count, int parameter_count, | 223 int fixed_register_count, int parameter_count, |
| 25 Handle<FixedArray> handler_table) { | 224 Handle<FixedArray> handler_table) { |
| 26 Flush(); | 225 Flush(); |
| 27 return next_stage_->ToBytecodeArray(fixed_register_count, parameter_count, | 226 return next_stage_->ToBytecodeArray(fixed_register_count, parameter_count, |
| 28 handler_table); | 227 handler_table); |
| 29 } | 228 } |
| 30 | 229 |
| 31 // override | 230 // 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 | |
| 47 void BytecodePeepholeOptimizer::BindLabel(BytecodeLabel* label) { | 231 void BytecodePeepholeOptimizer::BindLabel(BytecodeLabel* label) { |
| 48 Flush(); | 232 Flush(); |
| 49 next_stage_->BindLabel(label); | 233 next_stage_->BindLabel(label); |
| 50 } | 234 } |
| 51 | 235 |
| 52 // override | 236 // override |
| 53 void BytecodePeepholeOptimizer::BindLabel(const BytecodeLabel& target, | 237 void BytecodePeepholeOptimizer::BindLabel(const BytecodeLabel& target, |
| 54 BytecodeLabel* label) { | 238 BytecodeLabel* label) { |
| 55 // There is no need to flush here, it will have been flushed when |target| | 239 // There is no need to flush here, it will have been flushed when |
| 56 // was bound. | 240 // |target| was bound. |
| 57 next_stage_->BindLabel(target, label); | 241 next_stage_->BindLabel(target, label); |
| 58 } | 242 } |
| 59 | 243 |
| 244 // override | |
| 245 void BytecodePeepholeOptimizer::WriteJump(BytecodeNode* node, | |
| 246 BytecodeLabel* label) { | |
| 247 Optimize(node); | |
| 248 next_stage()->WriteJump(node, label); | |
| 249 } | |
| 250 | |
| 251 // override | |
| 252 void BytecodePeepholeOptimizer::Write(BytecodeNode* node) { Optimize(node); } | |
| 253 | |
| 60 void BytecodePeepholeOptimizer::Flush() { | 254 void BytecodePeepholeOptimizer::Flush() { |
| 61 // TODO(oth/rmcilroy): We could check CanElideLast() here to potentially | |
| 62 // eliminate last rather than writing it. | |
| 63 if (LastIsValid()) { | 255 if (LastIsValid()) { |
| 64 next_stage_->Write(&last_); | 256 next_stage_->Write(&last_); |
| 65 InvalidateLast(); | 257 InvalidateLast(); |
| 66 } | 258 } |
| 67 } | 259 } |
| 68 | 260 |
| 69 void BytecodePeepholeOptimizer::InvalidateLast() { | 261 void BytecodePeepholeOptimizer::InvalidateLast() { |
| 70 last_.set_bytecode(Bytecode::kIllegal); | 262 last_.set_bytecode(Bytecode::kIllegal); |
| 71 } | 263 } |
| 72 | 264 |
| 73 bool BytecodePeepholeOptimizer::LastIsValid() const { | 265 bool BytecodePeepholeOptimizer::LastIsValid() const { |
| 74 return last_.bytecode() != Bytecode::kIllegal; | 266 return last_.bytecode() != Bytecode::kIllegal; |
| 75 } | 267 } |
| 76 | 268 |
| 77 void BytecodePeepholeOptimizer::SetLast(const BytecodeNode* const node) { | 269 void BytecodePeepholeOptimizer::SetLast(const BytecodeNode* const node) { |
| 78 last_.Clone(node); | 270 last_.Clone(node); |
| 79 } | 271 } |
| 80 | 272 |
| 81 Handle<Object> BytecodePeepholeOptimizer::GetConstantForIndexOperand( | 273 Handle<Object> BytecodePeepholeOptimizer::GetConstantForIndexOperand( |
| 82 const BytecodeNode* const node, int index) const { | 274 const BytecodeNode* const node, int index) const { |
| 83 DCHECK_LE(index, node->operand_count()); | 275 DCHECK_LE(index, node->operand_count()); |
| 84 DCHECK_EQ(Bytecodes::GetOperandType(node->bytecode(), 0), OperandType::kIdx); | 276 DCHECK_EQ(Bytecodes::GetOperandType(node->bytecode(), 0), OperandType::kIdx); |
| 85 uint32_t index_operand = node->operand(0); | 277 uint32_t index_operand = node->operand(0); |
| 86 return constant_array_builder_->At(index_operand); | 278 return constant_array_builder_->At(index_operand); |
| 87 } | 279 } |
| 88 | 280 |
| 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) ToNumber if the last puts a number in the accumulator. | |
| 129 return false; | |
| 130 } | |
| 131 } | |
| 132 | |
| 133 bool BytecodePeepholeOptimizer::CanElideLastBasedOnSourcePosition( | 281 bool BytecodePeepholeOptimizer::CanElideLastBasedOnSourcePosition( |
| 134 const BytecodeNode* const current) const { | 282 const BytecodeNode* const current) const { |
| 135 // | 283 // |
| 136 // The rules for allowing the elision of the last bytecode based | 284 // The rules for allowing the elision of the last bytecode based |
| 137 // on source position are: | 285 // on source position are: |
| 138 // | 286 // |
| 139 // C U R R E N T | 287 // C U R R E N T |
| 140 // +--------+--------+--------+ | 288 // +--------+--------+--------+ |
| 141 // | None | Expr | Stmt | | 289 // | None | Expr | Stmt | |
| 142 // L +--------+--------+--------+--------+ | 290 // L +--------+--------+--------+--------+ |
| 143 // | None | YES | YES | YES | | 291 // | None | YES | YES | YES | |
| 144 // A +--------+--------+--------+--------+ | 292 // A +--------+--------+--------+--------+ |
| 145 // | Expr | YES | MAYBE | MAYBE | | 293 // | Expr | YES | MAYBE | MAYBE | |
| 146 // S +--------+--------+--------+--------+ | 294 // S +--------+--------+--------+--------+ |
| 147 // | Stmt | YES | NO | NO | | 295 // | Stmt | YES | NO | NO | |
| 148 // T +--------+--------+--------+--------+ | 296 // T +--------+--------+--------+--------+ |
| 149 // | 297 // |
| 150 // The goal is not lose any statement positions and not lose useful | 298 // The goal is not lose any statement positions and not lose useful |
| 151 // expression positions. Whenever the last bytecode is elided it's | 299 // expression positions. Whenever the last bytecode is elided it's |
| 152 // source position information is applied to the current node | 300 // source position information is applied to the current node |
| 153 // updating it if necessary. | 301 // updating it if necessary. |
| 154 // | 302 // |
| 155 // The last bytecode can be elided for the MAYBE cases if the last | 303 // The last bytecode can be elided for the MAYBE cases if the last |
| 156 // bytecode is known not to throw. If it throws, the system would | 304 // bytecode is known not to throw. If it throws, the system would |
| 157 // not have correct stack trace information. The appropriate check | 305 // not have correct stack trace information. The appropriate check |
| 158 // for this would be Bytecodes::IsWithoutExternalSideEffects(), | 306 // for this would be Bytecodes::IsWithoutExternalSideEffects(). |
| 159 // which is checked in | |
| 160 // BytecodePeepholeOptimizer::TransformLastAndCurrentBytecodes() to | |
| 161 // keep the check here simple. | |
| 162 // | 307 // |
| 163 // In rare cases, bytecode generation produces consecutive bytecodes | 308 // In rare cases, bytecode generation produces consecutive bytecodes |
| 164 // with the same expression positions. In principle, the latter of | 309 // with the same expression positions. In principle, the latter of |
| 165 // these can be elided, but would make this function more expensive. | 310 // these can be elided, but would make this function more expensive. |
| 166 // | 311 // |
| 167 return (!last_.source_info().is_valid() || | 312 return (!last_.source_info().is_valid() || |
| 168 !current->source_info().is_valid()); | 313 !current->source_info().is_valid()); |
| 169 } | 314 } |
| 170 | 315 |
| 171 namespace { | 316 namespace { |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 183 // which loads a global value into both a register and the | 328 // which loads a global value into both a register and the |
| 184 // accumulator. However, in the second form the Ldar can often be | 329 // accumulator. However, in the second form the Ldar can often be |
| 185 // peephole optimized away unlike the Star in the first form. | 330 // peephole optimized away unlike the Star in the first form. |
| 186 // | 331 // |
| 187 last->Transform(new_bytecode, current->operand(0)); | 332 last->Transform(new_bytecode, current->operand(0)); |
| 188 current->set_bytecode(Bytecode::kLdar, current->operand(0)); | 333 current->set_bytecode(Bytecode::kLdar, current->operand(0)); |
| 189 } | 334 } |
| 190 | 335 |
| 191 } // namespace | 336 } // namespace |
| 192 | 337 |
| 193 bool BytecodePeepholeOptimizer::TransformLastAndCurrentBytecodes( | 338 void BytecodePeepholeOptimizer::DefaultAction(BytecodeNode* const node) { |
| 194 BytecodeNode* const current) { | 339 DCHECK(LastIsValid()); |
| 195 if (current->bytecode() == Bytecode::kStar && | 340 DCHECK(!Bytecodes::IsJump(node->bytecode())); |
| 196 !current->source_info().is_statement()) { | 341 |
| 197 // Note: If the Star is tagged with a statement position, we can't | 342 next_stage()->Write(last()); |
| 198 // perform this transform as the store to the register will | 343 SetLast(node); |
| 199 // have the wrong ordering for stepping in the debugger. | |
| 200 switch (last_.bytecode()) { | |
| 201 case Bytecode::kLdaNamedProperty: | |
| 202 TransformLdaStarToLdrLdar(Bytecode::kLdrNamedProperty, &last_, current); | |
| 203 return true; | |
| 204 case Bytecode::kLdaKeyedProperty: | |
| 205 TransformLdaStarToLdrLdar(Bytecode::kLdrKeyedProperty, &last_, current); | |
| 206 return true; | |
| 207 case Bytecode::kLdaGlobal: | |
| 208 TransformLdaStarToLdrLdar(Bytecode::kLdrGlobal, &last_, current); | |
| 209 return true; | |
| 210 case Bytecode::kLdaContextSlot: | |
| 211 TransformLdaStarToLdrLdar(Bytecode::kLdrContextSlot, &last_, current); | |
| 212 return true; | |
| 213 case Bytecode::kLdaUndefined: | |
| 214 TransformLdaStarToLdrLdar(Bytecode::kLdrUndefined, &last_, current); | |
| 215 return true; | |
| 216 default: | |
| 217 break; | |
| 218 } | |
| 219 } | |
| 220 return false; | |
| 221 } | 344 } |
| 222 | 345 |
| 223 bool BytecodePeepholeOptimizer::RemoveToBooleanFromJump( | 346 void BytecodePeepholeOptimizer::UpdateLastAction(BytecodeNode* const node) { |
| 224 BytecodeNode* const current) { | 347 DCHECK(!LastIsValid()); |
| 225 bool can_remove = Bytecodes::IsJumpIfToBoolean(current->bytecode()) && | 348 DCHECK(!Bytecodes::IsJump(node->bytecode())); |
| 226 Bytecodes::WritesBooleanToAccumulator(last_.bytecode()); | 349 |
| 227 if (can_remove) { | 350 SetLast(node); |
| 228 // Conditional jumps with boolean conditions are emiitted in | |
| 229 // ToBoolean form by the bytecode array builder, | |
| 230 // i.e. JumpIfToBooleanTrue rather JumpIfTrue. The ToBoolean | |
| 231 // element can be removed if the previous bytecode put a boolean | |
| 232 // value in the accumulator. | |
| 233 Bytecode jump = Bytecodes::GetJumpWithoutToBoolean(current->bytecode()); | |
| 234 current->set_bytecode(jump, current->operand(0)); | |
| 235 } | |
| 236 return can_remove; | |
| 237 } | 351 } |
| 238 | 352 |
| 239 bool BytecodePeepholeOptimizer::RemoveToBooleanFromLogicalNot( | 353 void BytecodePeepholeOptimizer::ElideCurrentAction(BytecodeNode* const node) { |
| 240 BytecodeNode* const current) { | 354 DCHECK(LastIsValid()); |
| 241 bool can_remove = current->bytecode() == Bytecode::kToBooleanLogicalNot && | 355 DCHECK(!Bytecodes::IsJump(node->bytecode())); |
| 242 Bytecodes::WritesBooleanToAccumulator(last_.bytecode()); | |
| 243 if (can_remove) { | |
| 244 // Logical-nots are emitted in ToBoolean form by the bytecode array | |
| 245 // builder, The ToBoolean element can be removed if the previous bytecode | |
| 246 // put a boolean value in the accumulator. | |
| 247 current->set_bytecode(Bytecode::kLogicalNot); | |
| 248 } | |
| 249 return can_remove; | |
| 250 } | |
| 251 | 356 |
| 252 bool BytecodePeepholeOptimizer::TransformCurrentBytecode( | 357 if (node->source_info().is_valid()) { |
| 253 BytecodeNode* const current) { | 358 // Preserve the source information by replacing the node bytecode |
| 254 return RemoveToBooleanFromJump(current) || | 359 // with a no op bytecode. |
| 255 RemoveToBooleanFromLogicalNot(current); | 360 node->set_bytecode(Bytecode::kNop); |
| 256 } | 361 DefaultAction(node); |
| 257 | |
| 258 bool BytecodePeepholeOptimizer::CanElideLast( | |
| 259 const BytecodeNode* const current) const { | |
| 260 if (last_.bytecode() == Bytecode::kNop) { | |
| 261 // Nop are placeholders for holding source position information. | |
| 262 return true; | |
| 263 } else if (Bytecodes::IsAccumulatorLoadWithoutEffects(current->bytecode()) && | |
| 264 Bytecodes::IsAccumulatorLoadWithoutEffects(last_.bytecode())) { | |
| 265 // The accumulator is invisible to the debugger. If there is a sequence of | |
| 266 // consecutive accumulator loads (that don't have side effects) then only | |
| 267 // the final load is potentially visible. | |
| 268 return true; | |
| 269 } else if (Bytecodes::GetAccumulatorUse(current->bytecode()) == | |
| 270 AccumulatorUse::kWrite && | |
| 271 Bytecodes::IsAccumulatorLoadWithoutEffects(last_.bytecode())) { | |
| 272 // The current instruction clobbers the accumulator without reading it. The | |
| 273 // load in the last instruction can be elided as it has no effect. | |
| 274 return true; | |
| 275 } else { | 362 } else { |
| 276 return false; | 363 // Nothing to do, keep last and wait for next bytecode to pair with it. |
| 277 } | 364 } |
| 278 } | 365 } |
| 279 | 366 |
| 280 BytecodeNode* BytecodePeepholeOptimizer::Optimize(BytecodeNode* current) { | 367 void BytecodePeepholeOptimizer::ElideCurrentIfOperand0MatchesAction( |
| 281 TryToRemoveLastExpressionPosition(current); | 368 BytecodeNode* const node) { |
| 369 DCHECK(LastIsValid()); | |
| 370 DCHECK(!Bytecodes::IsJump(node->bytecode())); | |
| 282 | 371 |
| 283 if (TransformCurrentBytecode(current) || | 372 if (last()->operand(0) == node->operand(0)) { |
| 284 TransformLastAndCurrentBytecodes(current)) { | 373 ElideCurrentAction(node); |
| 285 return current; | 374 } else { |
| 375 DefaultAction(node); | |
| 286 } | 376 } |
| 287 | |
| 288 if (CanElideCurrent(current)) { | |
| 289 if (current->source_info().is_valid()) { | |
| 290 // Preserve the source information by replacing the current bytecode | |
| 291 // with a no op bytecode. | |
| 292 current->set_bytecode(Bytecode::kNop); | |
| 293 } else { | |
| 294 current = nullptr; | |
| 295 } | |
| 296 return current; | |
| 297 } | |
| 298 | |
| 299 if (CanElideLast(current) && CanElideLastBasedOnSourcePosition(current)) { | |
| 300 if (last_.source_info().is_valid()) { | |
| 301 // Current can not be valid per CanElideLastBasedOnSourcePosition(). | |
| 302 current->source_info().Clone(last_.source_info()); | |
| 303 } | |
| 304 InvalidateLast(); | |
| 305 return current; | |
| 306 } | |
| 307 | |
| 308 return current; | |
| 309 } | 377 } |
| 310 | 378 |
| 311 BytecodeNode* BytecodePeepholeOptimizer::OptimizeAndEmitLast( | 379 void BytecodePeepholeOptimizer::ElideCurrentIfLoadingNameConstantAction( |
| 312 BytecodeNode* current) { | 380 BytecodeNode* const node) { |
| 313 // Attempt optimization if there is an earlier node to optimize with. | 381 DCHECK_EQ(last()->bytecode(), Bytecode::kLdaConstant); |
| 314 if (LastIsValid()) { | 382 DCHECK(!Bytecodes::IsJump(node->bytecode())); |
| 315 current = Optimize(current); | 383 |
| 316 // Only output the last node if it wasn't invalidated by the optimization. | 384 if (GetConstantForIndexOperand(last(), 0)->IsName()) { |
| 317 if (LastIsValid()) { | 385 ElideCurrentAction(node); |
| 318 next_stage_->Write(&last_); | 386 } else { |
| 319 InvalidateLast(); | 387 DefaultAction(node); |
| 388 } | |
| 389 } | |
| 390 | |
| 391 void BytecodePeepholeOptimizer::ElideLastAction(BytecodeNode* const node) { | |
| 392 DCHECK(LastIsValid()); | |
| 393 DCHECK(!Bytecodes::IsJump(node->bytecode())); | |
| 394 | |
| 395 if (CanElideLastBasedOnSourcePosition(node)) { | |
| 396 if (last()->source_info().is_valid()) { | |
| 397 // Node can not be valid per CanElideLastBasedOnSourcePosition(). | |
| 398 node->source_info().Clone(last()->source_info()); | |
| 320 } | 399 } |
| 400 SetLast(node); | |
| 401 } else { | |
| 402 DefaultAction(node); | |
| 321 } | 403 } |
| 322 return current; | 404 } |
| 405 | |
| 406 void BytecodePeepholeOptimizer::ChangeBytecodeAction(BytecodeNode* node, | |
| 407 Bytecode replacement) { | |
| 408 DCHECK(LastIsValid()); | |
| 409 DCHECK(!Bytecodes::IsJump(node->bytecode())); | |
| 410 | |
| 411 node->set_bytecode(replacement); | |
| 412 DefaultAction(node); | |
| 413 } | |
| 414 | |
| 415 void BytecodePeepholeOptimizer::TransformLdaStarToLdrLdarAction( | |
| 416 BytecodeNode* node, Bytecode replacement) { | |
| 417 DCHECK(LastIsValid()); | |
| 418 DCHECK(!Bytecodes::IsJump(node->bytecode())); | |
| 419 | |
| 420 if (!node->source_info().is_statement()) { | |
| 421 TransformLdaStarToLdrLdar(replacement, last(), node); | |
| 422 } | |
| 423 DefaultAction(node); | |
| 424 } | |
| 425 | |
| 426 void BytecodePeepholeOptimizer::DefaultJumpAction(BytecodeNode* node) { | |
| 427 DCHECK(LastIsValid()); | |
| 428 DCHECK(Bytecodes::IsJump(node->bytecode())); | |
| 429 | |
| 430 next_stage()->Write(last()); | |
| 431 InvalidateLast(); | |
| 432 } | |
| 433 | |
| 434 void BytecodePeepholeOptimizer::UpdateLastJumpAction(BytecodeNode* node) { | |
| 435 DCHECK(!LastIsValid()); | |
| 436 DCHECK(Bytecodes::IsJump(node->bytecode())); | |
| 437 } | |
| 438 | |
| 439 void BytecodePeepholeOptimizer::ChangeJumpBytecodeAction(BytecodeNode* node, | |
| 440 Bytecode replacement) { | |
| 441 DCHECK(LastIsValid()); | |
| 442 DCHECK(Bytecodes::IsJump(node->bytecode())); | |
| 443 | |
| 444 next_stage()->Write(last()); | |
| 445 InvalidateLast(); | |
| 446 node->set_bytecode(replacement, node->operand(0)); | |
| 447 } | |
| 448 | |
| 449 void BytecodePeepholeOptimizer::ElideLastBeforeJumpAction(BytecodeNode* node) { | |
| 450 DCHECK(LastIsValid()); | |
| 451 DCHECK(Bytecodes::IsJump(node->bytecode())); | |
| 452 | |
| 453 if (!node->source_info().is_valid()) { | |
| 454 node->source_info().Clone(last()->source_info()); | |
| 455 } else { | |
| 456 next_stage()->Write(last()); | |
| 457 } | |
| 458 InvalidateLast(); | |
| 459 } | |
| 460 | |
| 461 void BytecodePeepholeOptimizer::Optimize(BytecodeNode* const node) { | |
| 462 // A single table is used for looking up peephole optimization | |
| 463 // matches as it is observed to have better performance. This is | |
| 464 // inspite of the fact that jump bytecodes and non-jump bytecodes | |
| 465 // have different processing logic, in particular a jump bytecode | |
| 466 // always needs to emit the jump via WriteJump(). | |
| 467 const PeepholeActionAndData* const action_data = | |
| 468 PeepholeActionTable::Lookup(last()->bytecode(), node->bytecode()); | |
|
rmcilroy
2016/07/04 12:45:46
You could possibly store function pointers in the
| |
| 469 switch (action_data->action) { | |
| 470 case PeepholeAction::kDefaultAction: | |
| 471 DefaultAction(node); | |
| 472 break; | |
| 473 case PeepholeAction::kUpdateLastAction: | |
| 474 UpdateLastAction(node); | |
| 475 break; | |
| 476 case PeepholeAction::kElideCurrentAction: | |
| 477 ElideCurrentAction(node); | |
| 478 break; | |
| 479 case PeepholeAction::kElideCurrentIfOperand0MatchesAction: | |
| 480 ElideCurrentIfOperand0MatchesAction(node); | |
| 481 break; | |
| 482 case PeepholeAction::kElideCurrentIfLoadingNameConstantAction: | |
| 483 ElideCurrentIfLoadingNameConstantAction(node); | |
| 484 break; | |
| 485 case PeepholeAction::kElideLastAction: | |
| 486 ElideLastAction(node); | |
| 487 break; | |
| 488 case PeepholeAction::kChangeBytecodeAction: | |
| 489 ChangeBytecodeAction(node, action_data->bytecode); | |
| 490 break; | |
| 491 case PeepholeAction::kTransformLdaStarToLdrLdarAction: | |
| 492 TransformLdaStarToLdrLdarAction(node, action_data->bytecode); | |
| 493 break; | |
| 494 case PeepholeAction::kDefaultJumpAction: | |
| 495 DefaultJumpAction(node); | |
| 496 break; | |
| 497 case PeepholeAction::kUpdateLastJumpAction: | |
| 498 UpdateLastJumpAction(node); | |
| 499 break; | |
| 500 case PeepholeAction::kChangeJumpBytecodeAction: | |
| 501 ChangeJumpBytecodeAction(node, action_data->bytecode); | |
| 502 break; | |
| 503 case PeepholeAction::kElideLastBeforeJumpAction: | |
| 504 ElideLastBeforeJumpAction(node); | |
| 505 break; | |
| 506 default: | |
| 507 UNREACHABLE(); | |
| 508 break; | |
| 509 } | |
| 323 } | 510 } |
| 324 | 511 |
| 325 } // namespace interpreter | 512 } // namespace interpreter |
| 326 } // namespace internal | 513 } // namespace internal |
| 327 } // namespace v8 | 514 } // namespace v8 |
| OLD | NEW |