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