OLD | NEW |
(Empty) | |
| 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/v8.h" |
| 6 |
| 7 #include "src/factory.h" |
| 8 #include "src/interpreter/bytecode-peephole-optimizer.h" |
| 9 #include "src/interpreter/constant-array-builder.h" |
| 10 #include "src/objects-inl.h" |
| 11 #include "src/objects.h" |
| 12 #include "test/unittests/test-utils.h" |
| 13 |
| 14 namespace v8 { |
| 15 namespace internal { |
| 16 namespace interpreter { |
| 17 |
| 18 class BytecodePeepholeOptimizerTest : public BytecodePipelineStage, |
| 19 public TestWithIsolateAndZone { |
| 20 public: |
| 21 BytecodePeepholeOptimizerTest() |
| 22 : constant_array_builder_(isolate(), zone()), |
| 23 peephole_optimizer_(&constant_array_builder_, this) {} |
| 24 ~BytecodePeepholeOptimizerTest() override {} |
| 25 |
| 26 size_t FlushForOffset() override { |
| 27 flush_for_offset_count_++; |
| 28 return 0; |
| 29 }; |
| 30 |
| 31 void FlushBasicBlock() override { flush_basic_block_count_++; } |
| 32 |
| 33 void Write(BytecodeNode* node) override { |
| 34 write_count_++; |
| 35 last_written_.Clone(node); |
| 36 } |
| 37 |
| 38 BytecodePeepholeOptimizer* optimizer() { return &peephole_optimizer_; } |
| 39 ConstantArrayBuilder* constant_array() { return &constant_array_builder_; } |
| 40 |
| 41 int flush_for_offset_count() const { return flush_for_offset_count_; } |
| 42 int flush_basic_block_count() const { return flush_basic_block_count_; } |
| 43 int write_count() const { return write_count_; } |
| 44 const BytecodeNode& last_written() const { return last_written_; } |
| 45 |
| 46 private: |
| 47 ConstantArrayBuilder constant_array_builder_; |
| 48 BytecodePeepholeOptimizer peephole_optimizer_; |
| 49 |
| 50 int flush_for_offset_count_ = 0; |
| 51 int flush_basic_block_count_ = 0; |
| 52 int write_count_ = 0; |
| 53 BytecodeNode last_written_; |
| 54 }; |
| 55 |
| 56 // Sanity tests. |
| 57 |
| 58 TEST_F(BytecodePeepholeOptimizerTest, FlushForOffsetPassThrough) { |
| 59 CHECK_EQ(flush_for_offset_count(), 0); |
| 60 CHECK_EQ(optimizer()->FlushForOffset(), 0); |
| 61 CHECK_EQ(flush_for_offset_count(), 1); |
| 62 } |
| 63 |
| 64 TEST_F(BytecodePeepholeOptimizerTest, FlushForOffsetRightSize) { |
| 65 BytecodeNode node(Bytecode::kAdd, Register(0).ToOperand(), |
| 66 OperandScale::kQuadruple); |
| 67 optimizer()->Write(&node); |
| 68 CHECK_EQ(optimizer()->FlushForOffset(), node.Size()); |
| 69 CHECK_EQ(flush_for_offset_count(), 1); |
| 70 CHECK_EQ(write_count(), 0); |
| 71 } |
| 72 |
| 73 TEST_F(BytecodePeepholeOptimizerTest, FlushForOffsetNop) { |
| 74 BytecodeNode node(Bytecode::kNop); |
| 75 optimizer()->Write(&node); |
| 76 CHECK_EQ(optimizer()->FlushForOffset(), 0); |
| 77 CHECK_EQ(flush_for_offset_count(), 1); |
| 78 CHECK_EQ(write_count(), 0); |
| 79 } |
| 80 |
| 81 TEST_F(BytecodePeepholeOptimizerTest, FlushForOffsetNopExpression) { |
| 82 BytecodeNode node(Bytecode::kNop); |
| 83 node.source_info().Update({3, false}); |
| 84 optimizer()->Write(&node); |
| 85 CHECK_EQ(optimizer()->FlushForOffset(), 0); |
| 86 CHECK_EQ(flush_for_offset_count(), 1); |
| 87 CHECK_EQ(write_count(), 0); |
| 88 } |
| 89 |
| 90 TEST_F(BytecodePeepholeOptimizerTest, FlushForOffsetNopStatement) { |
| 91 BytecodeNode node(Bytecode::kNop); |
| 92 node.source_info().Update({3, true}); |
| 93 optimizer()->Write(&node); |
| 94 CHECK_EQ(optimizer()->FlushForOffset(), node.Size()); |
| 95 CHECK_EQ(flush_for_offset_count(), 1); |
| 96 CHECK_EQ(write_count(), 0); |
| 97 } |
| 98 |
| 99 TEST_F(BytecodePeepholeOptimizerTest, FlushBasicBlockPassThrough) { |
| 100 CHECK_EQ(flush_basic_block_count(), 0); |
| 101 optimizer()->FlushBasicBlock(); |
| 102 CHECK_EQ(flush_basic_block_count(), 1); |
| 103 CHECK_EQ(write_count(), 0); |
| 104 } |
| 105 |
| 106 TEST_F(BytecodePeepholeOptimizerTest, WriteOneFlushBasicBlock) { |
| 107 BytecodeNode node(Bytecode::kAdd, Register(0).ToOperand(), |
| 108 OperandScale::kQuadruple); |
| 109 optimizer()->Write(&node); |
| 110 CHECK_EQ(write_count(), 0); |
| 111 optimizer()->FlushBasicBlock(); |
| 112 CHECK_EQ(write_count(), 1); |
| 113 CHECK_EQ(node, last_written()); |
| 114 } |
| 115 |
| 116 // Tests covering BytecodePeepholeOptimizer::UpdateCurrentBytecode(). |
| 117 |
| 118 TEST_F(BytecodePeepholeOptimizerTest, KeepJumpIfToBooleanTrue) { |
| 119 BytecodeNode first(Bytecode::kLdaNull); |
| 120 BytecodeNode second(Bytecode::kJumpIfToBooleanTrue, 3, OperandScale::kSingle); |
| 121 optimizer()->Write(&first); |
| 122 CHECK_EQ(write_count(), 0); |
| 123 optimizer()->Write(&second); |
| 124 CHECK_EQ(write_count(), 1); |
| 125 CHECK_EQ(last_written(), first); |
| 126 optimizer()->FlushBasicBlock(); |
| 127 CHECK_EQ(write_count(), 2); |
| 128 CHECK_EQ(last_written(), second); |
| 129 } |
| 130 |
| 131 TEST_F(BytecodePeepholeOptimizerTest, ElideJumpIfToBooleanTrue) { |
| 132 BytecodeNode first(Bytecode::kLdaTrue); |
| 133 BytecodeNode second(Bytecode::kJumpIfToBooleanTrue, 3, OperandScale::kSingle); |
| 134 optimizer()->Write(&first); |
| 135 CHECK_EQ(write_count(), 0); |
| 136 optimizer()->Write(&second); |
| 137 CHECK_EQ(write_count(), 1); |
| 138 CHECK_EQ(last_written(), first); |
| 139 optimizer()->FlushBasicBlock(); |
| 140 CHECK_EQ(write_count(), 2); |
| 141 CHECK_EQ(last_written().bytecode(), Bytecode::kJumpIfTrue); |
| 142 CHECK_EQ(last_written().operand(0), second.operand(0)); |
| 143 } |
| 144 |
| 145 // Tests covering BytecodePeepholeOptimizer::CanElideCurrent(). |
| 146 |
| 147 TEST_F(BytecodePeepholeOptimizerTest, LdarRxLdarRy) { |
| 148 BytecodeNode first(Bytecode::kLdar, Register(0).ToOperand(), |
| 149 OperandScale::kSingle); |
| 150 BytecodeNode second(Bytecode::kLdar, Register(1).ToOperand(), |
| 151 OperandScale::kSingle); |
| 152 optimizer()->Write(&first); |
| 153 optimizer()->FlushForOffset(); // Prevent CanElideLast removing |first|. |
| 154 CHECK_EQ(write_count(), 0); |
| 155 optimizer()->Write(&second); |
| 156 CHECK_EQ(write_count(), 1); |
| 157 CHECK_EQ(last_written(), first); |
| 158 optimizer()->FlushBasicBlock(); |
| 159 CHECK_EQ(write_count(), 2); |
| 160 CHECK_EQ(last_written(), second); |
| 161 } |
| 162 |
| 163 TEST_F(BytecodePeepholeOptimizerTest, LdarRxLdarRx) { |
| 164 BytecodeNode first(Bytecode::kLdar, Register(0).ToOperand(), |
| 165 OperandScale::kSingle); |
| 166 BytecodeNode second(Bytecode::kLdar, Register(0).ToOperand(), |
| 167 OperandScale::kSingle); |
| 168 optimizer()->Write(&first); |
| 169 CHECK_EQ(write_count(), 0); |
| 170 optimizer()->FlushForOffset(); // Prevent CanElideLast removing |first|. |
| 171 optimizer()->Write(&second); |
| 172 CHECK_EQ(write_count(), 1); |
| 173 CHECK_EQ(last_written(), first); |
| 174 optimizer()->FlushBasicBlock(); |
| 175 CHECK_EQ(write_count(), 1); |
| 176 } |
| 177 |
| 178 TEST_F(BytecodePeepholeOptimizerTest, LdarRxLdarRxStatement) { |
| 179 BytecodeNode first(Bytecode::kLdar, Register(0).ToOperand(), |
| 180 OperandScale::kSingle); |
| 181 BytecodeNode second(Bytecode::kLdar, Register(0).ToOperand(), |
| 182 OperandScale::kSingle); |
| 183 second.source_info().Update({0, true}); |
| 184 optimizer()->Write(&first); |
| 185 CHECK_EQ(write_count(), 0); |
| 186 optimizer()->FlushForOffset(); // Prevent CanElideLast removing |first|. |
| 187 optimizer()->Write(&second); |
| 188 CHECK_EQ(write_count(), 1); |
| 189 CHECK_EQ(last_written(), first); |
| 190 optimizer()->FlushBasicBlock(); |
| 191 CHECK_EQ(write_count(), 2); |
| 192 CHECK_EQ(last_written().bytecode(), Bytecode::kNop); |
| 193 CHECK_EQ(last_written().source_info(), second.source_info()); |
| 194 } |
| 195 |
| 196 TEST_F(BytecodePeepholeOptimizerTest, LdarRxLdarRxStatementStarRy) { |
| 197 BytecodeNode first(Bytecode::kLdar, Register(0).ToOperand(), |
| 198 OperandScale::kSingle); |
| 199 BytecodeNode second(Bytecode::kLdar, Register(0).ToOperand(), |
| 200 OperandScale::kSingle); |
| 201 BytecodeNode third(Bytecode::kStar, Register(3).ToOperand(), |
| 202 OperandScale::kSingle); |
| 203 second.source_info().Update({0, true}); |
| 204 optimizer()->Write(&first); |
| 205 CHECK_EQ(write_count(), 0); |
| 206 optimizer()->FlushForOffset(); // Prevent CanElideLast removing |first|. |
| 207 optimizer()->Write(&second); |
| 208 CHECK_EQ(write_count(), 1); |
| 209 CHECK_EQ(last_written(), first); |
| 210 optimizer()->Write(&third); |
| 211 CHECK_EQ(write_count(), 1); |
| 212 optimizer()->FlushBasicBlock(); |
| 213 CHECK_EQ(write_count(), 2); |
| 214 // Source position should move |second| to |third| when |second| is elided. |
| 215 third.source_info().Update(second.source_info()); |
| 216 CHECK_EQ(last_written(), third); |
| 217 } |
| 218 |
| 219 TEST_F(BytecodePeepholeOptimizerTest, LdarToName) { |
| 220 BytecodeNode first(Bytecode::kLdar, Register(0).ToOperand(), |
| 221 OperandScale::kSingle); |
| 222 BytecodeNode second(Bytecode::kToName); |
| 223 optimizer()->Write(&first); |
| 224 CHECK_EQ(write_count(), 0); |
| 225 optimizer()->Write(&second); |
| 226 CHECK_EQ(write_count(), 1); |
| 227 CHECK_EQ(last_written(), first); |
| 228 optimizer()->FlushBasicBlock(); |
| 229 CHECK_EQ(write_count(), 2); |
| 230 CHECK_EQ(last_written(), second); |
| 231 } |
| 232 |
| 233 TEST_F(BytecodePeepholeOptimizerTest, ToNameToName) { |
| 234 BytecodeNode first(Bytecode::kToName); |
| 235 BytecodeNode second(Bytecode::kToName); |
| 236 optimizer()->Write(&first); |
| 237 CHECK_EQ(write_count(), 0); |
| 238 optimizer()->Write(&second); |
| 239 CHECK_EQ(write_count(), 1); |
| 240 CHECK_EQ(last_written(), first); |
| 241 optimizer()->FlushBasicBlock(); |
| 242 CHECK_EQ(write_count(), 1); |
| 243 } |
| 244 |
| 245 TEST_F(BytecodePeepholeOptimizerTest, TypeOfToName) { |
| 246 BytecodeNode first(Bytecode::kTypeOf); |
| 247 BytecodeNode second(Bytecode::kToName); |
| 248 optimizer()->Write(&first); |
| 249 CHECK_EQ(write_count(), 0); |
| 250 optimizer()->Write(&second); |
| 251 CHECK_EQ(write_count(), 1); |
| 252 CHECK_EQ(last_written(), first); |
| 253 optimizer()->FlushBasicBlock(); |
| 254 CHECK_EQ(write_count(), 1); |
| 255 } |
| 256 |
| 257 TEST_F(BytecodePeepholeOptimizerTest, LdaConstantStringToName) { |
| 258 Handle<Object> word = |
| 259 isolate()->factory()->NewStringFromStaticChars("optimizing"); |
| 260 size_t index = constant_array()->Insert(word); |
| 261 BytecodeNode first(Bytecode::kLdaConstant, static_cast<uint32_t>(index), |
| 262 OperandScale::kSingle); |
| 263 BytecodeNode second(Bytecode::kToName); |
| 264 optimizer()->Write(&first); |
| 265 CHECK_EQ(write_count(), 0); |
| 266 optimizer()->Write(&second); |
| 267 CHECK_EQ(write_count(), 1); |
| 268 CHECK_EQ(last_written(), first); |
| 269 optimizer()->FlushBasicBlock(); |
| 270 CHECK_EQ(write_count(), 1); |
| 271 } |
| 272 |
| 273 TEST_F(BytecodePeepholeOptimizerTest, LdaConstantNumberToName) { |
| 274 Handle<Object> word = isolate()->factory()->NewNumber(0.380); |
| 275 size_t index = constant_array()->Insert(word); |
| 276 BytecodeNode first(Bytecode::kLdaConstant, static_cast<uint32_t>(index), |
| 277 OperandScale::kSingle); |
| 278 BytecodeNode second(Bytecode::kToName); |
| 279 optimizer()->Write(&first); |
| 280 CHECK_EQ(write_count(), 0); |
| 281 optimizer()->Write(&second); |
| 282 CHECK_EQ(write_count(), 1); |
| 283 CHECK_EQ(last_written(), first); |
| 284 optimizer()->FlushBasicBlock(); |
| 285 CHECK_EQ(write_count(), 2); |
| 286 CHECK_EQ(last_written(), second); |
| 287 } |
| 288 |
| 289 // Tests covering BytecodePeepholeOptimizer::CanElideLast(). |
| 290 |
| 291 TEST_F(BytecodePeepholeOptimizerTest, LdaTrueLdaFalseNotDiscardable) { |
| 292 BytecodeNode first(Bytecode::kLdaTrue); |
| 293 BytecodeNode second(Bytecode::kLdaFalse); |
| 294 optimizer()->Write(&first); |
| 295 optimizer()->FlushForOffset(); // Prevent discarding of |first|. |
| 296 CHECK_EQ(write_count(), 0); |
| 297 optimizer()->Write(&second); |
| 298 CHECK_EQ(write_count(), 1); |
| 299 CHECK_EQ(last_written(), first); |
| 300 optimizer()->FlushBasicBlock(); |
| 301 CHECK_EQ(write_count(), 2); |
| 302 CHECK_EQ(last_written(), second); |
| 303 } |
| 304 |
| 305 TEST_F(BytecodePeepholeOptimizerTest, LdaTrueLdaFalse) { |
| 306 BytecodeNode first(Bytecode::kLdaTrue); |
| 307 BytecodeNode second(Bytecode::kLdaFalse); |
| 308 optimizer()->Write(&first); |
| 309 CHECK_EQ(write_count(), 0); |
| 310 optimizer()->Write(&second); |
| 311 CHECK_EQ(write_count(), 0); |
| 312 optimizer()->FlushBasicBlock(); |
| 313 CHECK_EQ(write_count(), 1); |
| 314 CHECK_EQ(last_written(), second); |
| 315 } |
| 316 |
| 317 TEST_F(BytecodePeepholeOptimizerTest, LdaTrueStatementLdaFalse) { |
| 318 BytecodeNode first(Bytecode::kLdaTrue); |
| 319 first.source_info().Update({3, false}); |
| 320 BytecodeNode second(Bytecode::kLdaFalse); |
| 321 optimizer()->Write(&first); |
| 322 CHECK_EQ(write_count(), 0); |
| 323 optimizer()->Write(&second); |
| 324 CHECK_EQ(write_count(), 0); |
| 325 optimizer()->FlushBasicBlock(); |
| 326 CHECK_EQ(write_count(), 1); |
| 327 second.source_info().Update(first.source_info()); |
| 328 CHECK_EQ(last_written(), second); |
| 329 } |
| 330 |
| 331 TEST_F(BytecodePeepholeOptimizerTest, NopStackCheck) { |
| 332 BytecodeNode first(Bytecode::kNop); |
| 333 BytecodeNode second(Bytecode::kStackCheck); |
| 334 optimizer()->Write(&first); |
| 335 CHECK_EQ(write_count(), 0); |
| 336 optimizer()->Write(&second); |
| 337 CHECK_EQ(write_count(), 0); |
| 338 optimizer()->FlushBasicBlock(); |
| 339 CHECK_EQ(write_count(), 1); |
| 340 CHECK_EQ(last_written(), second); |
| 341 } |
| 342 |
| 343 TEST_F(BytecodePeepholeOptimizerTest, NopStatementStackCheck) { |
| 344 BytecodeNode first(Bytecode::kNop); |
| 345 first.source_info().Update({3, false}); |
| 346 BytecodeNode second(Bytecode::kStackCheck); |
| 347 optimizer()->Write(&first); |
| 348 CHECK_EQ(write_count(), 0); |
| 349 optimizer()->Write(&second); |
| 350 CHECK_EQ(write_count(), 0); |
| 351 optimizer()->FlushBasicBlock(); |
| 352 CHECK_EQ(write_count(), 1); |
| 353 second.source_info().Update(first.source_info()); |
| 354 CHECK_EQ(last_written(), second); |
| 355 } |
| 356 |
| 357 } // namespace interpreter |
| 358 } // namespace internal |
| 359 } // namespace v8 |
OLD | NEW |