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 |