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-label.h" 9#include "src/interpreter/bytecode-peephole-optimizer.h" 10#include "src/interpreter/constant-array-builder.h" 11#include "src/objects-inl.h" 12#include "src/objects.h" 13#include "test/unittests/test-utils.h" 14 15namespace v8 { 16namespace internal { 17namespace interpreter { 18 19class BytecodePeepholeOptimizerTest : public BytecodePipelineStage, 20 public TestWithIsolateAndZone { 21 public: 22 BytecodePeepholeOptimizerTest() 23 : constant_array_builder_(isolate(), zone()), 24 peephole_optimizer_(&constant_array_builder_, this) {} 25 ~BytecodePeepholeOptimizerTest() override {} 26 27 void Write(BytecodeNode* node) override { 28 write_count_++; 29 last_written_.Clone(node); 30 } 31 32 void WriteJump(BytecodeNode* node, BytecodeLabel* label) override { 33 write_count_++; 34 last_written_.Clone(node); 35 } 36 37 void BindLabel(BytecodeLabel* label) override {} 38 void BindLabel(const BytecodeLabel& target, BytecodeLabel* label) override {} 39 Handle<BytecodeArray> ToBytecodeArray( 40 int fixed_register_count, int parameter_count, 41 Handle<FixedArray> handle_table) override { 42 return Handle<BytecodeArray>(); 43 } 44 45 void Flush() { 46 optimizer()->ToBytecodeArray(0, 0, factory()->empty_fixed_array()); 47 } 48 49 BytecodePeepholeOptimizer* optimizer() { return &peephole_optimizer_; } 50 ConstantArrayBuilder* constant_array() { return &constant_array_builder_; } 51 52 int write_count() const { return write_count_; } 53 const BytecodeNode& last_written() const { return last_written_; } 54 55 private: 56 ConstantArrayBuilder constant_array_builder_; 57 BytecodePeepholeOptimizer peephole_optimizer_; 58 59 int write_count_ = 0; 60 BytecodeNode last_written_; 61}; 62 63// Sanity tests. 64 65TEST_F(BytecodePeepholeOptimizerTest, FlushOnJump) { 66 CHECK_EQ(write_count(), 0); 67 68 BytecodeNode add(Bytecode::kAdd, Register(0).ToOperand()); 69 optimizer()->Write(&add); 70 CHECK_EQ(write_count(), 0); 71 72 BytecodeLabel target; 73 BytecodeNode jump(Bytecode::kJump, 0); 74 optimizer()->WriteJump(&jump, &target); 75 CHECK_EQ(write_count(), 2); 76 CHECK_EQ(jump, last_written()); 77} 78 79TEST_F(BytecodePeepholeOptimizerTest, FlushOnBind) { 80 CHECK_EQ(write_count(), 0); 81 82 BytecodeNode add(Bytecode::kAdd, Register(0).ToOperand()); 83 optimizer()->Write(&add); 84 CHECK_EQ(write_count(), 0); 85 86 BytecodeLabel target; 87 optimizer()->BindLabel(&target); 88 CHECK_EQ(write_count(), 1); 89 CHECK_EQ(add, last_written()); 90} 91 92// Nop elimination tests. 93 94TEST_F(BytecodePeepholeOptimizerTest, ElideEmptyNop) { 95 BytecodeNode nop(Bytecode::kNop); 96 optimizer()->Write(&nop); 97 BytecodeNode add(Bytecode::kAdd, Register(0).ToOperand()); 98 optimizer()->Write(&add); 99 Flush(); 100 CHECK_EQ(write_count(), 1); 101 CHECK_EQ(add, last_written()); 102} 103 104TEST_F(BytecodePeepholeOptimizerTest, ElideExpressionNop) { 105 BytecodeNode nop(Bytecode::kNop); 106 nop.source_info().MakeExpressionPosition(3); 107 optimizer()->Write(&nop); 108 BytecodeNode add(Bytecode::kAdd, Register(0).ToOperand()); 109 optimizer()->Write(&add); 110 Flush(); 111 CHECK_EQ(write_count(), 1); 112 CHECK_EQ(add, last_written()); 113} 114 115TEST_F(BytecodePeepholeOptimizerTest, KeepStatementNop) { 116 BytecodeNode nop(Bytecode::kNop); 117 nop.source_info().MakeStatementPosition(3); 118 optimizer()->Write(&nop); 119 BytecodeNode add(Bytecode::kAdd, Register(0).ToOperand()); 120 add.source_info().MakeExpressionPosition(3); 121 optimizer()->Write(&add); 122 Flush(); 123 CHECK_EQ(write_count(), 2); 124 CHECK_EQ(add, last_written()); 125} 126 127// Tests covering BytecodePeepholeOptimizer::UpdateCurrentBytecode(). 128 129TEST_F(BytecodePeepholeOptimizerTest, KeepJumpIfToBooleanTrue) { 130 BytecodeNode first(Bytecode::kLdaNull); 131 BytecodeNode second(Bytecode::kJumpIfToBooleanTrue, 3); 132 optimizer()->Write(&first); 133 CHECK_EQ(write_count(), 0); 134 optimizer()->Write(&second); 135 CHECK_EQ(write_count(), 1); 136 CHECK_EQ(last_written(), first); 137 Flush(); 138 CHECK_EQ(write_count(), 2); 139 CHECK_EQ(last_written(), second); 140} 141 142TEST_F(BytecodePeepholeOptimizerTest, ElideJumpIfToBooleanTrue) { 143 BytecodeNode first(Bytecode::kLdaTrue); 144 BytecodeNode second(Bytecode::kJumpIfToBooleanTrue, 3); 145 optimizer()->Write(&first); 146 CHECK_EQ(write_count(), 0); 147 optimizer()->Write(&second); 148 CHECK_EQ(write_count(), 1); 149 CHECK_EQ(last_written(), first); 150 Flush(); 151 CHECK_EQ(write_count(), 2); 152 CHECK_EQ(last_written().bytecode(), Bytecode::kJumpIfTrue); 153 CHECK_EQ(last_written().operand(0), second.operand(0)); 154} 155 156TEST_F(BytecodePeepholeOptimizerTest, KeepToBooleanLogicalNot) { 157 BytecodeNode first(Bytecode::kLdaNull); 158 BytecodeNode second(Bytecode::kToBooleanLogicalNot); 159 optimizer()->Write(&first); 160 CHECK_EQ(write_count(), 0); 161 optimizer()->Write(&second); 162 CHECK_EQ(write_count(), 1); 163 CHECK_EQ(last_written(), first); 164 Flush(); 165 CHECK_EQ(write_count(), 2); 166 CHECK_EQ(last_written(), second); 167} 168 169TEST_F(BytecodePeepholeOptimizerTest, ElideToBooleanLogicalNot) { 170 BytecodeNode first(Bytecode::kLdaTrue); 171 BytecodeNode second(Bytecode::kToBooleanLogicalNot); 172 optimizer()->Write(&first); 173 CHECK_EQ(write_count(), 0); 174 optimizer()->Write(&second); 175 CHECK_EQ(write_count(), 1); 176 CHECK_EQ(last_written(), first); 177 Flush(); 178 CHECK_EQ(write_count(), 2); 179 CHECK_EQ(last_written().bytecode(), Bytecode::kLogicalNot); 180} 181 182// Tests covering BytecodePeepholeOptimizer::CanElideCurrent(). 183 184TEST_F(BytecodePeepholeOptimizerTest, StarRxLdarRy) { 185 BytecodeNode first(Bytecode::kStar, Register(0).ToOperand()); 186 BytecodeNode second(Bytecode::kLdar, Register(1).ToOperand()); 187 optimizer()->Write(&first); 188 CHECK_EQ(write_count(), 0); 189 optimizer()->Write(&second); 190 CHECK_EQ(write_count(), 1); 191 CHECK_EQ(last_written(), first); 192 Flush(); 193 CHECK_EQ(write_count(), 2); 194 CHECK_EQ(last_written(), second); 195} 196 197TEST_F(BytecodePeepholeOptimizerTest, StarRxLdarRx) { 198 BytecodeLabel label; 199 BytecodeNode first(Bytecode::kStar, Register(0).ToOperand()); 200 BytecodeNode second(Bytecode::kLdar, Register(0).ToOperand()); 201 optimizer()->Write(&first); 202 CHECK_EQ(write_count(), 0); 203 optimizer()->Write(&second); 204 CHECK_EQ(write_count(), 1); 205 CHECK_EQ(last_written(), first); 206 Flush(); 207 CHECK_EQ(write_count(), 1); 208} 209 210TEST_F(BytecodePeepholeOptimizerTest, StarRxLdarRxStatement) { 211 BytecodeNode first(Bytecode::kStar, Register(0).ToOperand()); 212 BytecodeNode second(Bytecode::kLdar, Register(0).ToOperand()); 213 second.source_info().MakeStatementPosition(0); 214 optimizer()->Write(&first); 215 CHECK_EQ(write_count(), 0); 216 optimizer()->Write(&second); 217 CHECK_EQ(write_count(), 1); 218 CHECK_EQ(last_written(), first); 219 Flush(); 220 CHECK_EQ(write_count(), 2); 221 CHECK_EQ(last_written().bytecode(), Bytecode::kNop); 222 CHECK_EQ(last_written().source_info(), second.source_info()); 223} 224 225TEST_F(BytecodePeepholeOptimizerTest, StarRxLdarRxStatementStarRy) { 226 BytecodeLabel label; 227 BytecodeNode first(Bytecode::kStar, Register(0).ToOperand()); 228 BytecodeNode second(Bytecode::kLdar, Register(0).ToOperand()); 229 BytecodeNode third(Bytecode::kStar, Register(3).ToOperand()); 230 second.source_info().MakeStatementPosition(0); 231 optimizer()->Write(&first); 232 CHECK_EQ(write_count(), 0); 233 optimizer()->Write(&second); 234 CHECK_EQ(write_count(), 1); 235 CHECK_EQ(last_written(), first); 236 optimizer()->Write(&third); 237 CHECK_EQ(write_count(), 1); 238 Flush(); 239 CHECK_EQ(write_count(), 2); 240 CHECK_EQ(last_written(), third); 241} 242 243TEST_F(BytecodePeepholeOptimizerTest, LdarToName) { 244 BytecodeNode first(Bytecode::kLdar, Register(0).ToOperand()); 245 BytecodeNode second(Bytecode::kToName); 246 optimizer()->Write(&first); 247 CHECK_EQ(write_count(), 0); 248 optimizer()->Write(&second); 249 CHECK_EQ(write_count(), 1); 250 CHECK_EQ(last_written(), first); 251 Flush(); 252 CHECK_EQ(write_count(), 2); 253 CHECK_EQ(last_written(), second); 254} 255 256TEST_F(BytecodePeepholeOptimizerTest, ToNameToName) { 257 BytecodeNode first(Bytecode::kToName); 258 BytecodeNode second(Bytecode::kToName); 259 optimizer()->Write(&first); 260 CHECK_EQ(write_count(), 0); 261 optimizer()->Write(&second); 262 CHECK_EQ(write_count(), 1); 263 CHECK_EQ(last_written(), first); 264 Flush(); 265 CHECK_EQ(write_count(), 1); 266} 267 268TEST_F(BytecodePeepholeOptimizerTest, TypeOfToName) { 269 BytecodeNode first(Bytecode::kTypeOf); 270 BytecodeNode second(Bytecode::kToName); 271 optimizer()->Write(&first); 272 CHECK_EQ(write_count(), 0); 273 optimizer()->Write(&second); 274 CHECK_EQ(write_count(), 1); 275 CHECK_EQ(last_written(), first); 276 Flush(); 277 CHECK_EQ(write_count(), 1); 278} 279 280TEST_F(BytecodePeepholeOptimizerTest, LdaConstantStringToName) { 281 Handle<Object> word = 282 isolate()->factory()->NewStringFromStaticChars("optimizing"); 283 size_t index = constant_array()->Insert(word); 284 BytecodeNode first(Bytecode::kLdaConstant, static_cast<uint32_t>(index)); 285 BytecodeNode second(Bytecode::kToName); 286 optimizer()->Write(&first); 287 CHECK_EQ(write_count(), 0); 288 optimizer()->Write(&second); 289 CHECK_EQ(write_count(), 1); 290 CHECK_EQ(last_written(), first); 291 Flush(); 292 CHECK_EQ(write_count(), 1); 293} 294 295TEST_F(BytecodePeepholeOptimizerTest, LdaConstantNumberToName) { 296 Handle<Object> word = isolate()->factory()->NewNumber(0.380); 297 size_t index = constant_array()->Insert(word); 298 BytecodeNode first(Bytecode::kLdaConstant, static_cast<uint32_t>(index)); 299 BytecodeNode second(Bytecode::kToName); 300 optimizer()->Write(&first); 301 CHECK_EQ(write_count(), 0); 302 optimizer()->Write(&second); 303 CHECK_EQ(write_count(), 1); 304 CHECK_EQ(last_written(), first); 305 Flush(); 306 CHECK_EQ(write_count(), 2); 307 CHECK_EQ(last_written(), second); 308} 309 310// Tests covering BytecodePeepholeOptimizer::CanElideLast(). 311 312TEST_F(BytecodePeepholeOptimizerTest, LdaTrueLdaFalse) { 313 BytecodeNode first(Bytecode::kLdaTrue); 314 BytecodeNode second(Bytecode::kLdaFalse); 315 optimizer()->Write(&first); 316 CHECK_EQ(write_count(), 0); 317 optimizer()->Write(&second); 318 CHECK_EQ(write_count(), 0); 319 Flush(); 320 CHECK_EQ(write_count(), 1); 321 CHECK_EQ(last_written(), second); 322} 323 324TEST_F(BytecodePeepholeOptimizerTest, LdaTrueStatementLdaFalse) { 325 BytecodeNode first(Bytecode::kLdaTrue); 326 first.source_info().MakeExpressionPosition(3); 327 BytecodeNode second(Bytecode::kLdaFalse); 328 optimizer()->Write(&first); 329 CHECK_EQ(write_count(), 0); 330 optimizer()->Write(&second); 331 CHECK_EQ(write_count(), 0); 332 Flush(); 333 CHECK_EQ(write_count(), 1); 334 CHECK_EQ(last_written(), second); 335 CHECK(second.source_info().is_expression()); 336 CHECK_EQ(second.source_info().source_position(), 3); 337} 338 339TEST_F(BytecodePeepholeOptimizerTest, NopStackCheck) { 340 BytecodeNode first(Bytecode::kNop); 341 BytecodeNode second(Bytecode::kStackCheck); 342 optimizer()->Write(&first); 343 CHECK_EQ(write_count(), 0); 344 optimizer()->Write(&second); 345 CHECK_EQ(write_count(), 0); 346 Flush(); 347 CHECK_EQ(write_count(), 1); 348 CHECK_EQ(last_written(), second); 349} 350 351TEST_F(BytecodePeepholeOptimizerTest, NopStatementStackCheck) { 352 BytecodeNode first(Bytecode::kNop); 353 first.source_info().MakeExpressionPosition(3); 354 BytecodeNode second(Bytecode::kStackCheck); 355 optimizer()->Write(&first); 356 CHECK_EQ(write_count(), 0); 357 optimizer()->Write(&second); 358 CHECK_EQ(write_count(), 0); 359 Flush(); 360 CHECK_EQ(write_count(), 1); 361 second.source_info().MakeExpressionPosition( 362 first.source_info().source_position()); 363 CHECK_EQ(last_written(), second); 364} 365 366// Tests covering BytecodePeepholeOptimizer::UpdateLastAndCurrentBytecodes(). 367 368TEST_F(BytecodePeepholeOptimizerTest, MergeLoadICStar) { 369 const uint32_t operands[] = { 370 static_cast<uint32_t>(Register(31).ToOperand()), 32, 33, 371 static_cast<uint32_t>(Register(256).ToOperand())}; 372 const int expected_operand_count = static_cast<int>(arraysize(operands)); 373 374 BytecodeNode first(Bytecode::kLdaNamedProperty, operands[0], operands[1], 375 operands[2]); 376 BytecodeNode second(Bytecode::kStar, operands[3]); 377 BytecodeNode third(Bytecode::kReturn); 378 optimizer()->Write(&first); 379 optimizer()->Write(&second); 380 CHECK_EQ(write_count(), 1); 381 CHECK_EQ(last_written().bytecode(), Bytecode::kLdrNamedProperty); 382 CHECK_EQ(last_written().operand_count(), expected_operand_count); 383 for (int i = 0; i < expected_operand_count; ++i) { 384 CHECK_EQ(last_written().operand(i), operands[i]); 385 } 386 optimizer()->Write(&third); 387 CHECK_EQ(write_count(), 2); 388 CHECK_EQ(last_written().bytecode(), Bytecode::kLdar); 389 CHECK_EQ(last_written().operand(0), operands[expected_operand_count - 1]); 390 Flush(); 391 CHECK_EQ(last_written().bytecode(), third.bytecode()); 392} 393 394TEST_F(BytecodePeepholeOptimizerTest, MergeLdaKeyedPropertyStar) { 395 const uint32_t operands[] = {static_cast<uint32_t>(Register(31).ToOperand()), 396 9999997, 397 static_cast<uint32_t>(Register(1).ToOperand())}; 398 const int expected_operand_count = static_cast<int>(arraysize(operands)); 399 400 BytecodeNode first(Bytecode::kLdaKeyedProperty, operands[0], operands[1]); 401 BytecodeNode second(Bytecode::kStar, operands[2]); 402 BytecodeNode third(Bytecode::kReturn); 403 optimizer()->Write(&first); 404 optimizer()->Write(&second); 405 CHECK_EQ(write_count(), 1); 406 CHECK_EQ(last_written().bytecode(), Bytecode::kLdrKeyedProperty); 407 CHECK_EQ(last_written().operand_count(), expected_operand_count); 408 for (int i = 0; i < expected_operand_count; ++i) { 409 CHECK_EQ(last_written().operand(i), operands[i]); 410 } 411 optimizer()->Write(&third); 412 CHECK_EQ(write_count(), 2); 413 CHECK_EQ(last_written().bytecode(), Bytecode::kLdar); 414 CHECK_EQ(last_written().operand(0), operands[expected_operand_count - 1]); 415 Flush(); 416 CHECK_EQ(last_written().bytecode(), third.bytecode()); 417} 418 419TEST_F(BytecodePeepholeOptimizerTest, MergeLdaGlobalStar) { 420 const uint32_t operands[] = {19191, 421 static_cast<uint32_t>(Register(1).ToOperand())}; 422 const int expected_operand_count = static_cast<int>(arraysize(operands)); 423 424 BytecodeNode first(Bytecode::kLdaGlobal, operands[0]); 425 BytecodeNode second(Bytecode::kStar, operands[1]); 426 BytecodeNode third(Bytecode::kReturn); 427 optimizer()->Write(&first); 428 optimizer()->Write(&second); 429 CHECK_EQ(write_count(), 1); 430 CHECK_EQ(last_written().bytecode(), Bytecode::kLdrGlobal); 431 CHECK_EQ(last_written().operand_count(), expected_operand_count); 432 for (int i = 0; i < expected_operand_count; ++i) { 433 CHECK_EQ(last_written().operand(i), operands[i]); 434 } 435 optimizer()->Write(&third); 436 CHECK_EQ(write_count(), 2); 437 CHECK_EQ(last_written().bytecode(), Bytecode::kLdar); 438 CHECK_EQ(last_written().operand(0), operands[expected_operand_count - 1]); 439 Flush(); 440 CHECK_EQ(last_written().bytecode(), third.bytecode()); 441} 442 443TEST_F(BytecodePeepholeOptimizerTest, MergeLdaContextSlotStar) { 444 const uint32_t operands[] = { 445 static_cast<uint32_t>(Register(200000).ToOperand()), 55005500, 446 static_cast<uint32_t>(Register(1).ToOperand())}; 447 const int expected_operand_count = static_cast<int>(arraysize(operands)); 448 449 BytecodeNode first(Bytecode::kLdaContextSlot, operands[0], operands[1]); 450 BytecodeNode second(Bytecode::kStar, operands[2]); 451 BytecodeNode third(Bytecode::kReturn); 452 optimizer()->Write(&first); 453 optimizer()->Write(&second); 454 CHECK_EQ(write_count(), 1); 455 CHECK_EQ(last_written().bytecode(), Bytecode::kLdrContextSlot); 456 CHECK_EQ(last_written().operand_count(), expected_operand_count); 457 for (int i = 0; i < expected_operand_count; ++i) { 458 CHECK_EQ(last_written().operand(i), operands[i]); 459 } 460 optimizer()->Write(&third); 461 CHECK_EQ(write_count(), 2); 462 CHECK_EQ(last_written().bytecode(), Bytecode::kLdar); 463 CHECK_EQ(last_written().operand(0), operands[expected_operand_count - 1]); 464 Flush(); 465 CHECK_EQ(last_written().bytecode(), third.bytecode()); 466} 467 468TEST_F(BytecodePeepholeOptimizerTest, MergeLdaUndefinedStar) { 469 const uint32_t operands[] = { 470 static_cast<uint32_t>(Register(100000).ToOperand())}; 471 const int expected_operand_count = static_cast<int>(arraysize(operands)); 472 473 BytecodeNode first(Bytecode::kLdaUndefined); 474 BytecodeNode second(Bytecode::kStar, operands[0]); 475 BytecodeNode third(Bytecode::kReturn); 476 optimizer()->Write(&first); 477 optimizer()->Write(&second); 478 CHECK_EQ(write_count(), 1); 479 CHECK_EQ(last_written().bytecode(), Bytecode::kLdrUndefined); 480 CHECK_EQ(last_written().operand_count(), expected_operand_count); 481 for (int i = 0; i < expected_operand_count; ++i) { 482 CHECK_EQ(last_written().operand(i), operands[i]); 483 } 484 optimizer()->Write(&third); 485 CHECK_EQ(write_count(), 2); 486 CHECK_EQ(last_written().bytecode(), Bytecode::kLdar); 487 CHECK_EQ(last_written().operand(0), operands[expected_operand_count - 1]); 488 Flush(); 489 CHECK_EQ(last_written().bytecode(), third.bytecode()); 490} 491 492} // namespace interpreter 493} // namespace internal 494} // namespace v8 495