| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/flow_graph_optimizer.h" | 5 #include "vm/flow_graph_optimizer.h" |
| 6 | 6 |
| 7 #include "vm/bit_vector.h" | 7 #include "vm/bit_vector.h" |
| 8 #include "vm/cha.h" | 8 #include "vm/cha.h" |
| 9 #include "vm/flow_graph_builder.h" | 9 #include "vm/flow_graph_builder.h" |
| 10 #include "vm/flow_graph_compiler.h" | 10 #include "vm/flow_graph_compiler.h" |
| (...skipping 356 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 367 GrowableArray<intptr_t> class_ids; | 367 GrowableArray<intptr_t> class_ids; |
| 368 Function& target = Function::Handle(); | 368 Function& target = Function::Handle(); |
| 369 for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) { | 369 for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) { |
| 370 ic_data.GetCheckAt(i, &class_ids, &target); | 370 ic_data.GetCheckAt(i, &class_ids, &target); |
| 371 if (class_ids[arg_n] != kSmiCid) return false; | 371 if (class_ids[arg_n] != kSmiCid) return false; |
| 372 } | 372 } |
| 373 return true; | 373 return true; |
| 374 } | 374 } |
| 375 | 375 |
| 376 | 376 |
| 377 bool FlowGraphOptimizer::TryReplaceWithArrayOp(InstanceCallInstr* call, | 377 // Returns array classid to load from, array and idnex value |
| 378 Token::Kind op_kind) { | 378 |
| 379 intptr_t FlowGraphOptimizer::PrepareIndexedOp(InstanceCallInstr* call, |
| 380 intptr_t class_id, |
| 381 Value** array, |
| 382 Value** index) { |
| 383 *array = call->ArgumentAt(0)->value(); |
| 384 *index = call->ArgumentAt(1)->value(); |
| 385 // Insert class check and index smi checks and attach a copy of the |
| 386 // original environment because the operation can still deoptimize. |
| 387 AddCheckClass(call, (*array)->Copy()); |
| 388 InsertBefore(call, |
| 389 new CheckSmiInstr((*index)->Copy(), call->deopt_id()), |
| 390 call->env(), |
| 391 Definition::kEffect); |
| 392 // If both index and array are constants, then the bound check always |
| 393 // succeeded. |
| 394 // TODO(srdjan): Remove once constant propagation lands. |
| 395 if (!((*array)->BindsToConstant() && (*index)->BindsToConstant())) { |
| 396 // Insert array bounds check. |
| 397 InsertBefore(call, |
| 398 new CheckArrayBoundInstr((*array)->Copy(), |
| 399 (*index)->Copy(), |
| 400 class_id, |
| 401 call), |
| 402 call->env(), |
| 403 Definition::kEffect); |
| 404 } |
| 405 if (class_id == kGrowableObjectArrayCid) { |
| 406 // Insert data elements load. |
| 407 LoadFieldInstr* elements = |
| 408 new LoadFieldInstr((*array)->Copy(), |
| 409 GrowableObjectArray::data_offset(), |
| 410 Type::ZoneHandle(Type::DynamicType())); |
| 411 elements->set_result_cid(kArrayCid); |
| 412 InsertBefore(call, elements, NULL, Definition::kValue); |
| 413 *array = new Value(elements); |
| 414 return kArrayCid; |
| 415 } |
| 416 return class_id; |
| 417 } |
| 418 |
| 419 |
| 420 bool FlowGraphOptimizer::TryReplaceWithStoreIndexed(InstanceCallInstr* call) { |
| 379 // TODO(fschneider): Optimize []= operator in checked mode as well. | 421 // TODO(fschneider): Optimize []= operator in checked mode as well. |
| 380 if (op_kind == Token::kASSIGN_INDEX && FLAG_enable_type_checks) return false; | 422 if (FLAG_enable_type_checks) return false; |
| 381 | |
| 382 const intptr_t class_id = ReceiverClassId(call); | 423 const intptr_t class_id = ReceiverClassId(call); |
| 424 ICData& value_check = ICData::Handle(); |
| 383 switch (class_id) { | 425 switch (class_id) { |
| 384 case kImmutableArrayCid: | |
| 385 // Stores are only specialized for Array and GrowableObjectArray, | |
| 386 // not for ImmutableArray. | |
| 387 if (op_kind == Token::kASSIGN_INDEX) return false; | |
| 388 // Fall through. | |
| 389 case kArrayCid: | 426 case kArrayCid: |
| 390 case kGrowableObjectArrayCid: { | 427 case kGrowableObjectArrayCid: |
| 391 Value* array = call->ArgumentAt(0)->value(); | 428 // Acceptable store index classes. |
| 392 Value* index = call->ArgumentAt(1)->value(); | 429 break; |
| 393 // Insert class check and index smi checks and attach a copy of the | 430 case kFloat64ArrayCid: { |
| 394 // original environment because the operation can still deoptimize. | 431 // Check that value is always double. |
| 395 AddCheckClass(call, array->Copy()); | 432 value_check = call->ic_data()->AsUnaryClassChecksForArgNr(2); |
| 396 InsertBefore(call, | 433 if ((value_check.NumberOfChecks() != 1) || |
| 397 new CheckSmiInstr(index->Copy(), call->deopt_id()), | 434 (value_check.GetReceiverClassIdAt(0) != kDoubleCid)) { |
| 398 call->env(), | 435 return false; |
| 399 Definition::kEffect); | |
| 400 // If both index and array are constants, then the bound check always | |
| 401 // succeeded. | |
| 402 // TODO(srdjan): Remove once constant propagation lands. | |
| 403 if (!(array->BindsToConstant() && index->BindsToConstant())) { | |
| 404 // Insert array bounds check. | |
| 405 InsertBefore(call, | |
| 406 new CheckArrayBoundInstr(array->Copy(), | |
| 407 index->Copy(), | |
| 408 class_id, | |
| 409 call), | |
| 410 call->env(), | |
| 411 Definition::kEffect); | |
| 412 } | 436 } |
| 413 if (class_id == kGrowableObjectArrayCid) { | 437 break; |
| 414 // Insert data elements load. | |
| 415 LoadFieldInstr* elements = | |
| 416 new LoadFieldInstr(array->Copy(), | |
| 417 GrowableObjectArray::data_offset(), | |
| 418 Type::ZoneHandle(Type::DynamicType())); | |
| 419 elements->set_result_cid(kArrayCid); | |
| 420 InsertBefore(call, elements, NULL, Definition::kValue); | |
| 421 array = new Value(elements); | |
| 422 } | |
| 423 Definition* array_op = NULL; | |
| 424 if (op_kind == Token::kINDEX) { | |
| 425 array_op = new LoadIndexedInstr(array, index); | |
| 426 } else { | |
| 427 bool needs_store_barrier = true; | |
| 428 if (ArgIsAlwaysSmi(*call->ic_data(), 2)) { | |
| 429 InsertBefore(call, | |
| 430 new CheckSmiInstr(call->ArgumentAt(2)->value()->Copy(), | |
| 431 call->deopt_id()), | |
| 432 call->env(), | |
| 433 Definition::kEffect); | |
| 434 needs_store_barrier = false; | |
| 435 } | |
| 436 Value* value = call->ArgumentAt(2)->value(); | |
| 437 array_op = | |
| 438 new StoreIndexedInstr(array, index, value, needs_store_barrier); | |
| 439 } | |
| 440 call->ReplaceWith(array_op, current_iterator()); | |
| 441 RemovePushArguments(call); | |
| 442 return true; | |
| 443 } | 438 } |
| 444 default: | 439 default: |
| 445 return false; | 440 return false; |
| 446 } | 441 } |
| 442 Value* array = NULL; |
| 443 Value* index = NULL; |
| 444 intptr_t array_cid = PrepareIndexedOp(call, class_id, &array, &index); |
| 445 Value* value = call->ArgumentAt(2)->value(); |
| 446 // Check if store barrier is needed. |
| 447 bool needs_store_barrier = true; |
| 448 if (class_id == kFloat64ArrayCid) { |
| 449 ASSERT(!value_check.IsNull()); |
| 450 InsertBefore(call, |
| 451 new CheckClassInstr(value->Copy(), |
| 452 call->deopt_id(), |
| 453 value_check), |
| 454 call->env(), |
| 455 Definition::kEffect); |
| 456 needs_store_barrier = false; |
| 457 } else if (ArgIsAlwaysSmi(*call->ic_data(), 2)) { |
| 458 InsertBefore(call, |
| 459 new CheckSmiInstr(value->Copy(), call->deopt_id()), |
| 460 call->env(), |
| 461 Definition::kEffect); |
| 462 needs_store_barrier = false; |
| 463 } |
| 464 |
| 465 Definition* array_op = |
| 466 new StoreIndexedInstr(array, index, value, |
| 467 needs_store_barrier, array_cid, call->deopt_id()); |
| 468 call->ReplaceWith(array_op, current_iterator()); |
| 469 RemovePushArguments(call); |
| 470 return true; |
| 471 } |
| 472 |
| 473 |
| 474 |
| 475 bool FlowGraphOptimizer::TryReplaceWithLoadIndexed(InstanceCallInstr* call) { |
| 476 const intptr_t class_id = ReceiverClassId(call); |
| 477 switch (class_id) { |
| 478 case kArrayCid: |
| 479 case kImmutableArrayCid: |
| 480 case kGrowableObjectArrayCid: |
| 481 case kFloat64ArrayCid: |
| 482 // Acceptable load index classes. |
| 483 break; |
| 484 default: |
| 485 return false; |
| 486 } |
| 487 Value* array = NULL; |
| 488 Value* index = NULL; |
| 489 intptr_t array_cid = PrepareIndexedOp(call, class_id, &array, &index); |
| 490 Definition* array_op = new LoadIndexedInstr(array, index, array_cid); |
| 491 call->ReplaceWith(array_op, current_iterator()); |
| 492 RemovePushArguments(call); |
| 493 return true; |
| 447 } | 494 } |
| 448 | 495 |
| 449 | 496 |
| 450 void FlowGraphOptimizer::InsertBefore(Instruction* next, | 497 void FlowGraphOptimizer::InsertBefore(Instruction* next, |
| 451 Instruction* instr, | 498 Instruction* instr, |
| 452 Environment* env, | 499 Environment* env, |
| 453 Definition::UseKind use_kind) { | 500 Definition::UseKind use_kind) { |
| 454 if (env != NULL) env->DeepCopyTo(instr); | 501 if (env != NULL) env->DeepCopyTo(instr); |
| 455 if (use_kind == Definition::kValue) { | 502 if (use_kind == Definition::kValue) { |
| 456 ASSERT(instr->IsDefinition()); | 503 ASSERT(instr->IsDefinition()); |
| (...skipping 496 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 953 | 1000 |
| 954 return false; | 1001 return false; |
| 955 } | 1002 } |
| 956 | 1003 |
| 957 | 1004 |
| 958 // Tries to optimize instance call by replacing it with a faster instruction | 1005 // Tries to optimize instance call by replacing it with a faster instruction |
| 959 // (e.g, binary op, field load, ..). | 1006 // (e.g, binary op, field load, ..). |
| 960 void FlowGraphOptimizer::VisitInstanceCall(InstanceCallInstr* instr) { | 1007 void FlowGraphOptimizer::VisitInstanceCall(InstanceCallInstr* instr) { |
| 961 if (instr->HasICData() && (instr->ic_data()->NumberOfChecks() > 0)) { | 1008 if (instr->HasICData() && (instr->ic_data()->NumberOfChecks() > 0)) { |
| 962 const Token::Kind op_kind = instr->token_kind(); | 1009 const Token::Kind op_kind = instr->token_kind(); |
| 963 if (Token::IsIndexOperator(op_kind) && | 1010 if ((op_kind == Token::kASSIGN_INDEX) && |
| 964 TryReplaceWithArrayOp(instr, op_kind)) { | 1011 TryReplaceWithStoreIndexed(instr)) { |
| 1012 return; |
| 1013 } |
| 1014 if ((op_kind == Token::kINDEX) && TryReplaceWithLoadIndexed(instr)) { |
| 965 return; | 1015 return; |
| 966 } | 1016 } |
| 967 if (Token::IsBinaryToken(op_kind) && | 1017 if (Token::IsBinaryToken(op_kind) && |
| 968 TryReplaceWithBinaryOp(instr, op_kind)) { | 1018 TryReplaceWithBinaryOp(instr, op_kind)) { |
| 969 return; | 1019 return; |
| 970 } | 1020 } |
| 971 if (Token::IsUnaryToken(op_kind) && | 1021 if (Token::IsUnaryToken(op_kind) && |
| 972 TryReplaceWithUnaryOp(instr, op_kind)) { | 1022 TryReplaceWithUnaryOp(instr, op_kind)) { |
| 973 return; | 1023 return; |
| 974 } | 1024 } |
| (...skipping 2303 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3278 | 3328 |
| 3279 if (FLAG_trace_constant_propagation) { | 3329 if (FLAG_trace_constant_propagation) { |
| 3280 OS::Print("\n==== After constant propagation ====\n"); | 3330 OS::Print("\n==== After constant propagation ====\n"); |
| 3281 FlowGraphPrinter printer(*graph_); | 3331 FlowGraphPrinter printer(*graph_); |
| 3282 printer.PrintBlocks(); | 3332 printer.PrintBlocks(); |
| 3283 } | 3333 } |
| 3284 } | 3334 } |
| 3285 | 3335 |
| 3286 | 3336 |
| 3287 } // namespace dart | 3337 } // namespace dart |
| OLD | NEW |