| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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/aot_optimizer.h" | 5 #include "vm/aot_optimizer.h" |
| 6 | 6 |
| 7 #include "vm/bit_vector.h" | 7 #include "vm/bit_vector.h" |
| 8 #include "vm/branch_optimizer.h" | 8 #include "vm/branch_optimizer.h" |
| 9 #include "vm/cha.h" | 9 #include "vm/cha.h" |
| 10 #include "vm/compiler.h" | 10 #include "vm/compiler.h" |
| (...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 253 return true; | 253 return true; |
| 254 } | 254 } |
| 255 } | 255 } |
| 256 } | 256 } |
| 257 | 257 |
| 258 return false; | 258 return false; |
| 259 } | 259 } |
| 260 | 260 |
| 261 | 261 |
| 262 const ICData& AotOptimizer::TrySpecializeICData(const ICData& ic_data, | 262 const ICData& AotOptimizer::TrySpecializeICData(const ICData& ic_data, |
| 263 intptr_t cid) { | 263 intptr_t cid) { |
| 264 ASSERT(ic_data.NumArgsTested() == 1); | 264 ASSERT(ic_data.NumArgsTested() == 1); |
| 265 | 265 |
| 266 if ((ic_data.NumberOfUsedChecks() == 1) && ic_data.HasReceiverClassId(cid)) { | 266 if ((ic_data.NumberOfUsedChecks() == 1) && ic_data.HasReceiverClassId(cid)) { |
| 267 return ic_data; // Nothing to do | 267 return ic_data; // Nothing to do |
| 268 } | 268 } |
| 269 | 269 |
| 270 const Function& function = | 270 const Function& function = |
| 271 Function::Handle(Z, ic_data.GetTargetForReceiverClassId(cid)); | 271 Function::Handle(Z, ic_data.GetTargetForReceiverClassId(cid)); |
| 272 // TODO(fschneider): Try looking up the function on the class if it is | 272 // TODO(fschneider): Try looking up the function on the class if it is |
| 273 // not found in the ICData. | 273 // not found in the ICData. |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 368 Token::kBIT_AND, | 368 Token::kBIT_AND, |
| 369 new(Z) Value(left_instr), | 369 new(Z) Value(left_instr), |
| 370 new(Z) Value(right_instr), | 370 new(Z) Value(right_instr), |
| 371 Thread::kNoDeoptId); // BIT_AND cannot deoptimize. | 371 Thread::kNoDeoptId); // BIT_AND cannot deoptimize. |
| 372 bit_and_instr->ReplaceWith(smi_op, current_iterator()); | 372 bit_and_instr->ReplaceWith(smi_op, current_iterator()); |
| 373 } | 373 } |
| 374 } | 374 } |
| 375 | 375 |
| 376 | 376 |
| 377 void AotOptimizer::AppendExtractNthOutputForMerged(Definition* instr, | 377 void AotOptimizer::AppendExtractNthOutputForMerged(Definition* instr, |
| 378 intptr_t index, | 378 intptr_t index, |
| 379 Representation rep, | 379 Representation rep, |
| 380 intptr_t cid) { | 380 intptr_t cid) { |
| 381 ExtractNthOutputInstr* extract = | 381 ExtractNthOutputInstr* extract = |
| 382 new(Z) ExtractNthOutputInstr(new(Z) Value(instr), index, rep, cid); | 382 new(Z) ExtractNthOutputInstr(new(Z) Value(instr), index, rep, cid); |
| 383 instr->ReplaceUsesWith(extract); | 383 instr->ReplaceUsesWith(extract); |
| 384 flow_graph()->InsertAfter(instr, extract, NULL, FlowGraph::kValue); | 384 flow_graph()->InsertAfter(instr, extract, NULL, FlowGraph::kValue); |
| 385 } | 385 } |
| 386 | 386 |
| 387 | 387 |
| 388 // Dart: | 388 // Dart: |
| 389 // var x = d % 10; | 389 // var x = d % 10; |
| 390 // var y = d ~/ 10; | 390 // var y = d ~/ 10; |
| (...skipping 313 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 704 if (ICDataHasReceiverArgumentClassIds(ic_data, kSmiCid, kSmiCid)) { | 704 if (ICDataHasReceiverArgumentClassIds(ic_data, kSmiCid, kSmiCid)) { |
| 705 return false; | 705 return false; |
| 706 } | 706 } |
| 707 | 707 |
| 708 // Check that it have seen only smis and doubles. | 708 // Check that it have seen only smis and doubles. |
| 709 return HasTwoDoubleOrSmi(ic_data); | 709 return HasTwoDoubleOrSmi(ic_data); |
| 710 } | 710 } |
| 711 | 711 |
| 712 | 712 |
| 713 void AotOptimizer::ReplaceCall(Definition* call, | 713 void AotOptimizer::ReplaceCall(Definition* call, |
| 714 Definition* replacement) { | 714 Definition* replacement) { |
| 715 // Remove the original push arguments. | 715 // Remove the original push arguments. |
| 716 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) { | 716 for (intptr_t i = 0; i < call->ArgumentCount(); ++i) { |
| 717 PushArgumentInstr* push = call->PushArgumentAt(i); | 717 PushArgumentInstr* push = call->PushArgumentAt(i); |
| 718 push->ReplaceUsesWith(push->value()->definition()); | 718 push->ReplaceUsesWith(push->value()->definition()); |
| 719 push->RemoveFromGraph(); | 719 push->RemoveFromGraph(); |
| 720 } | 720 } |
| 721 call->ReplaceWith(replacement, current_iterator()); | 721 call->ReplaceWith(replacement, current_iterator()); |
| 722 } | 722 } |
| 723 | 723 |
| 724 | 724 |
| 725 void AotOptimizer::AddCheckSmi(Definition* to_check, | 725 void AotOptimizer::AddCheckSmi(Definition* to_check, |
| 726 intptr_t deopt_id, | 726 intptr_t deopt_id, |
| 727 Environment* deopt_environment, | 727 Environment* deopt_environment, |
| 728 Instruction* insert_before) { | 728 Instruction* insert_before) { |
| 729 if (to_check->Type()->ToCid() != kSmiCid) { | 729 if (to_check->Type()->ToCid() != kSmiCid) { |
| 730 InsertBefore(insert_before, | 730 InsertBefore(insert_before, |
| 731 new(Z) CheckSmiInstr(new(Z) Value(to_check), | 731 new(Z) CheckSmiInstr(new(Z) Value(to_check), |
| 732 deopt_id, | 732 deopt_id, |
| 733 insert_before->token_pos()), | 733 insert_before->token_pos()), |
| 734 deopt_environment, | 734 deopt_environment, |
| 735 FlowGraph::kEffect); | 735 FlowGraph::kEffect); |
| 736 } | 736 } |
| 737 } | 737 } |
| 738 | 738 |
| 739 | 739 |
| 740 Instruction* AotOptimizer::GetCheckClass(Definition* to_check, | 740 Instruction* AotOptimizer::GetCheckClass(Definition* to_check, |
| 741 const ICData& unary_checks, | 741 const ICData& unary_checks, |
| 742 intptr_t deopt_id, | 742 intptr_t deopt_id, |
| 743 TokenPosition token_pos) { | 743 TokenPosition token_pos) { |
| 744 if ((unary_checks.NumberOfUsedChecks() == 1) && | 744 if ((unary_checks.NumberOfUsedChecks() == 1) && |
| 745 unary_checks.HasReceiverClassId(kSmiCid)) { | 745 unary_checks.HasReceiverClassId(kSmiCid)) { |
| 746 return new(Z) CheckSmiInstr(new(Z) Value(to_check), | 746 return new(Z) CheckSmiInstr(new(Z) Value(to_check), |
| 747 deopt_id, | 747 deopt_id, |
| 748 token_pos); | 748 token_pos); |
| 749 } | 749 } |
| 750 return new(Z) CheckClassInstr( | 750 return new(Z) CheckClassInstr( |
| 751 new(Z) Value(to_check), deopt_id, unary_checks, token_pos); | 751 new(Z) Value(to_check), deopt_id, unary_checks, token_pos); |
| 752 } | 752 } |
| 753 | 753 |
| 754 | 754 |
| 755 void AotOptimizer::AddCheckClass(Definition* to_check, | 755 void AotOptimizer::AddCheckClass(Definition* to_check, |
| 756 const ICData& unary_checks, | 756 const ICData& unary_checks, |
| 757 intptr_t deopt_id, | 757 intptr_t deopt_id, |
| 758 Environment* deopt_environment, | 758 Environment* deopt_environment, |
| 759 Instruction* insert_before) { | 759 Instruction* insert_before) { |
| 760 // Type propagation has not run yet, we cannot eliminate the check. | 760 // Type propagation has not run yet, we cannot eliminate the check. |
| 761 Instruction* check = GetCheckClass( | 761 Instruction* check = GetCheckClass( |
| 762 to_check, unary_checks, deopt_id, insert_before->token_pos()); | 762 to_check, unary_checks, deopt_id, insert_before->token_pos()); |
| 763 InsertBefore(insert_before, check, deopt_environment, FlowGraph::kEffect); | 763 InsertBefore(insert_before, check, deopt_environment, FlowGraph::kEffect); |
| 764 } | 764 } |
| 765 | 765 |
| 766 | 766 |
| 767 void AotOptimizer::AddReceiverCheck(InstanceCallInstr* call) { | 767 void AotOptimizer::AddReceiverCheck(InstanceCallInstr* call) { |
| 768 AddCheckClass(call->ArgumentAt(0), | 768 AddCheckClass(call->ArgumentAt(0), |
| 769 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecks()), | 769 ICData::ZoneHandle(Z, call->ic_data()->AsUnaryClassChecks()), |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 816 return d->IsStringFromCharCode(); | 816 return d->IsStringFromCharCode(); |
| 817 } | 817 } |
| 818 } | 818 } |
| 819 | 819 |
| 820 | 820 |
| 821 // Returns true if the string comparison was converted into char-code | 821 // Returns true if the string comparison was converted into char-code |
| 822 // comparison. Conversion is only possible for strings of length one. | 822 // comparison. Conversion is only possible for strings of length one. |
| 823 // E.g., detect str[x] == "x"; and use an integer comparison of char-codes. | 823 // E.g., detect str[x] == "x"; and use an integer comparison of char-codes. |
| 824 // TODO(srdjan): Expand for two-byte and external strings. | 824 // TODO(srdjan): Expand for two-byte and external strings. |
| 825 bool AotOptimizer::TryStringLengthOneEquality(InstanceCallInstr* call, | 825 bool AotOptimizer::TryStringLengthOneEquality(InstanceCallInstr* call, |
| 826 Token::Kind op_kind) { | 826 Token::Kind op_kind) { |
| 827 ASSERT(HasOnlyTwoOf(*call->ic_data(), kOneByteStringCid)); | 827 ASSERT(HasOnlyTwoOf(*call->ic_data(), kOneByteStringCid)); |
| 828 // Check that left and right are length one strings (either string constants | 828 // Check that left and right are length one strings (either string constants |
| 829 // or results of string-from-char-code. | 829 // or results of string-from-char-code. |
| 830 Definition* left = call->ArgumentAt(0); | 830 Definition* left = call->ArgumentAt(0); |
| 831 Definition* right = call->ArgumentAt(1); | 831 Definition* right = call->ArgumentAt(1); |
| 832 Value* left_val = NULL; | 832 Value* left_val = NULL; |
| 833 Definition* to_remove_left = NULL; | 833 Definition* to_remove_left = NULL; |
| 834 if (IsLengthOneString(right)) { | 834 if (IsLengthOneString(right)) { |
| 835 // Swap, since we know that both arguments are strings | 835 // Swap, since we know that both arguments are strings |
| 836 Definition* temp = left; | 836 Definition* temp = left; |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 903 } | 903 } |
| 904 return true; | 904 return true; |
| 905 } | 905 } |
| 906 return false; | 906 return false; |
| 907 } | 907 } |
| 908 | 908 |
| 909 | 909 |
| 910 static bool SmiFitsInDouble() { return kSmiBits < 53; } | 910 static bool SmiFitsInDouble() { return kSmiBits < 53; } |
| 911 | 911 |
| 912 bool AotOptimizer::TryReplaceWithEqualityOp(InstanceCallInstr* call, | 912 bool AotOptimizer::TryReplaceWithEqualityOp(InstanceCallInstr* call, |
| 913 Token::Kind op_kind) { | 913 Token::Kind op_kind) { |
| 914 const ICData& ic_data = *call->ic_data(); | 914 const ICData& ic_data = *call->ic_data(); |
| 915 ASSERT(ic_data.NumArgsTested() == 2); | 915 ASSERT(ic_data.NumArgsTested() == 2); |
| 916 | 916 |
| 917 ASSERT(call->ArgumentCount() == 2); | 917 ASSERT(call->ArgumentCount() == 2); |
| 918 Definition* left = call->ArgumentAt(0); | 918 Definition* left = call->ArgumentAt(0); |
| 919 Definition* right = call->ArgumentAt(1); | 919 Definition* right = call->ArgumentAt(1); |
| 920 | 920 |
| 921 intptr_t cid = kIllegalCid; | 921 intptr_t cid = kIllegalCid; |
| 922 if (HasOnlyTwoOf(ic_data, kOneByteStringCid)) { | 922 if (HasOnlyTwoOf(ic_data, kOneByteStringCid)) { |
| 923 if (TryStringLengthOneEquality(call, op_kind)) { | 923 if (TryStringLengthOneEquality(call, op_kind)) { |
| (...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1012 new(Z) Value(left), | 1012 new(Z) Value(left), |
| 1013 new(Z) Value(right), | 1013 new(Z) Value(right), |
| 1014 cid, | 1014 cid, |
| 1015 call->deopt_id()); | 1015 call->deopt_id()); |
| 1016 ReplaceCall(call, comp); | 1016 ReplaceCall(call, comp); |
| 1017 return true; | 1017 return true; |
| 1018 } | 1018 } |
| 1019 | 1019 |
| 1020 | 1020 |
| 1021 bool AotOptimizer::TryReplaceWithRelationalOp(InstanceCallInstr* call, | 1021 bool AotOptimizer::TryReplaceWithRelationalOp(InstanceCallInstr* call, |
| 1022 Token::Kind op_kind) { | 1022 Token::Kind op_kind) { |
| 1023 const ICData& ic_data = *call->ic_data(); | 1023 const ICData& ic_data = *call->ic_data(); |
| 1024 ASSERT(ic_data.NumArgsTested() == 2); | 1024 ASSERT(ic_data.NumArgsTested() == 2); |
| 1025 | 1025 |
| 1026 ASSERT(call->ArgumentCount() == 2); | 1026 ASSERT(call->ArgumentCount() == 2); |
| 1027 Definition* left = call->ArgumentAt(0); | 1027 Definition* left = call->ArgumentAt(0); |
| 1028 Definition* right = call->ArgumentAt(1); | 1028 Definition* right = call->ArgumentAt(1); |
| 1029 | 1029 |
| 1030 intptr_t cid = kIllegalCid; | 1030 intptr_t cid = kIllegalCid; |
| 1031 if (HasOnlyTwoOf(ic_data, kSmiCid)) { | 1031 if (HasOnlyTwoOf(ic_data, kSmiCid)) { |
| 1032 InsertBefore(call, | 1032 InsertBefore(call, |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1074 new(Z) Value(left), | 1074 new(Z) Value(left), |
| 1075 new(Z) Value(right), | 1075 new(Z) Value(right), |
| 1076 cid, | 1076 cid, |
| 1077 call->deopt_id()); | 1077 call->deopt_id()); |
| 1078 ReplaceCall(call, comp); | 1078 ReplaceCall(call, comp); |
| 1079 return true; | 1079 return true; |
| 1080 } | 1080 } |
| 1081 | 1081 |
| 1082 | 1082 |
| 1083 bool AotOptimizer::TryReplaceWithBinaryOp(InstanceCallInstr* call, | 1083 bool AotOptimizer::TryReplaceWithBinaryOp(InstanceCallInstr* call, |
| 1084 Token::Kind op_kind) { | 1084 Token::Kind op_kind) { |
| 1085 intptr_t operands_type = kIllegalCid; | 1085 intptr_t operands_type = kIllegalCid; |
| 1086 ASSERT(call->HasICData()); | 1086 ASSERT(call->HasICData()); |
| 1087 const ICData& ic_data = *call->ic_data(); | 1087 const ICData& ic_data = *call->ic_data(); |
| 1088 switch (op_kind) { | 1088 switch (op_kind) { |
| 1089 case Token::kADD: | 1089 case Token::kADD: |
| 1090 case Token::kSUB: | 1090 case Token::kSUB: |
| 1091 case Token::kMUL: | 1091 case Token::kMUL: |
| 1092 if (HasOnlyTwoOf(ic_data, kSmiCid)) { | 1092 if (HasOnlyTwoOf(ic_data, kSmiCid)) { |
| 1093 // Don't generate smi code if the IC data is marked because | 1093 // Don't generate smi code if the IC data is marked because |
| 1094 // of an overflow. | 1094 // of an overflow. |
| (...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1283 new(Z) Value(left), | 1283 new(Z) Value(left), |
| 1284 new(Z) Value(right), | 1284 new(Z) Value(right), |
| 1285 call->deopt_id()); | 1285 call->deopt_id()); |
| 1286 ReplaceCall(call, bin_op); | 1286 ReplaceCall(call, bin_op); |
| 1287 } | 1287 } |
| 1288 return true; | 1288 return true; |
| 1289 } | 1289 } |
| 1290 | 1290 |
| 1291 | 1291 |
| 1292 bool AotOptimizer::TryReplaceWithUnaryOp(InstanceCallInstr* call, | 1292 bool AotOptimizer::TryReplaceWithUnaryOp(InstanceCallInstr* call, |
| 1293 Token::Kind op_kind) { | 1293 Token::Kind op_kind) { |
| 1294 ASSERT(call->ArgumentCount() == 1); | 1294 ASSERT(call->ArgumentCount() == 1); |
| 1295 Definition* input = call->ArgumentAt(0); | 1295 Definition* input = call->ArgumentAt(0); |
| 1296 Definition* unary_op = NULL; | 1296 Definition* unary_op = NULL; |
| 1297 if (HasOnlyOneSmi(*call->ic_data())) { | 1297 if (HasOnlyOneSmi(*call->ic_data())) { |
| 1298 InsertBefore(call, | 1298 InsertBefore(call, |
| 1299 new(Z) CheckSmiInstr(new(Z) Value(input), | 1299 new(Z) CheckSmiInstr(new(Z) Value(input), |
| 1300 call->deopt_id(), | 1300 call->deopt_id(), |
| 1301 call->token_pos()), | 1301 call->token_pos()), |
| 1302 call->env(), | 1302 call->env(), |
| 1303 FlowGraph::kEffect); | 1303 FlowGraph::kEffect); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 1318 return false; | 1318 return false; |
| 1319 } | 1319 } |
| 1320 ASSERT(unary_op != NULL); | 1320 ASSERT(unary_op != NULL); |
| 1321 ReplaceCall(call, unary_op); | 1321 ReplaceCall(call, unary_op); |
| 1322 return true; | 1322 return true; |
| 1323 } | 1323 } |
| 1324 | 1324 |
| 1325 | 1325 |
| 1326 // Using field class | 1326 // Using field class |
| 1327 RawField* AotOptimizer::GetField(intptr_t class_id, | 1327 RawField* AotOptimizer::GetField(intptr_t class_id, |
| 1328 const String& field_name) { | 1328 const String& field_name) { |
| 1329 Class& cls = Class::Handle(Z, isolate()->class_table()->At(class_id)); | 1329 Class& cls = Class::Handle(Z, isolate()->class_table()->At(class_id)); |
| 1330 Field& field = Field::Handle(Z); | 1330 Field& field = Field::Handle(Z); |
| 1331 while (!cls.IsNull()) { | 1331 while (!cls.IsNull()) { |
| 1332 field = cls.LookupInstanceField(field_name); | 1332 field = cls.LookupInstanceField(field_name); |
| 1333 if (!field.IsNull()) { | 1333 if (!field.IsNull()) { |
| 1334 return field.raw(); | 1334 return field.raw(); |
| 1335 } | 1335 } |
| 1336 cls = cls.SuperClass(); | 1336 cls = cls.SuperClass(); |
| 1337 } | 1337 } |
| 1338 return Field::null(); | 1338 return Field::null(); |
| (...skipping 27 matching lines...) Expand all Loading... |
| 1366 name.ToCString(), cls.ToCString()); | 1366 name.ToCString(), cls.ToCString()); |
| 1367 } | 1367 } |
| 1368 thread()->cha()->AddToLeafClasses(cls); | 1368 thread()->cha()->AddToLeafClasses(cls); |
| 1369 return false; | 1369 return false; |
| 1370 } | 1370 } |
| 1371 } | 1371 } |
| 1372 return true; | 1372 return true; |
| 1373 } | 1373 } |
| 1374 | 1374 |
| 1375 | 1375 |
| 1376 bool AotOptimizer::InlineImplicitInstanceGetter(InstanceCallInstr* call, | 1376 bool AotOptimizer::InlineImplicitInstanceGetter(InstanceCallInstr* call) { |
| 1377 bool allow_check) { | |
| 1378 ASSERT(call->HasICData()); | 1377 ASSERT(call->HasICData()); |
| 1379 const ICData& ic_data = *call->ic_data(); | 1378 const ICData& ic_data = *call->ic_data(); |
| 1380 ASSERT(ic_data.HasOneTarget()); | 1379 ASSERT(ic_data.HasOneTarget()); |
| 1381 GrowableArray<intptr_t> class_ids; | 1380 GrowableArray<intptr_t> class_ids; |
| 1382 ic_data.GetClassIdsAt(0, &class_ids); | 1381 ic_data.GetClassIdsAt(0, &class_ids); |
| 1383 ASSERT(class_ids.length() == 1); | 1382 ASSERT(class_ids.length() == 1); |
| 1384 // Inline implicit instance getter. | 1383 // Inline implicit instance getter. |
| 1385 const String& field_name = | 1384 const String& field_name = |
| 1386 String::Handle(Z, Field::NameFromGetter(call->function_name())); | 1385 String::Handle(Z, Field::NameFromGetter(call->function_name())); |
| 1387 const Field& field = | 1386 const Field& field = |
| 1388 Field::ZoneHandle(Z, GetField(class_ids[0], field_name)); | 1387 Field::ZoneHandle(Z, GetField(class_ids[0], field_name)); |
| 1389 ASSERT(!field.IsNull()); | 1388 ASSERT(!field.IsNull()); |
| 1390 | 1389 |
| 1391 if (InstanceCallNeedsClassCheck(call, RawFunction::kImplicitGetter)) { | 1390 if (InstanceCallNeedsClassCheck(call, RawFunction::kImplicitGetter)) { |
| 1392 if (!allow_check) { | 1391 return false; |
| 1393 return false; | |
| 1394 } | |
| 1395 AddReceiverCheck(call); | |
| 1396 } | 1392 } |
| 1397 LoadFieldInstr* load = new(Z) LoadFieldInstr( | 1393 LoadFieldInstr* load = new(Z) LoadFieldInstr( |
| 1398 new(Z) Value(call->ArgumentAt(0)), | 1394 new(Z) Value(call->ArgumentAt(0)), |
| 1399 &field, | 1395 &field, |
| 1400 AbstractType::ZoneHandle(Z, field.type()), | 1396 AbstractType::ZoneHandle(Z, field.type()), |
| 1401 call->token_pos()); | 1397 call->token_pos()); |
| 1402 load->set_is_immutable(field.is_final()); | 1398 load->set_is_immutable(field.is_final()); |
| 1403 | 1399 |
| 1404 // No guarded cid in precompiled mode. | 1400 // No guarded cid in precompiled mode. |
| 1405 ASSERT(field.guarded_cid() == kDynamicCid); | 1401 ASSERT(field.guarded_cid() == kDynamicCid); |
| 1406 | 1402 |
| 1407 // Discard the environment from the original instruction because the load | 1403 // Discard the environment from the original instruction because the load |
| 1408 // can't deoptimize. | 1404 // can't deoptimize. |
| 1409 call->RemoveEnvironment(); | 1405 call->RemoveEnvironment(); |
| 1410 ReplaceCall(call, load); | 1406 ReplaceCall(call, load); |
| 1411 | 1407 |
| 1412 if (load->result_cid() != kDynamicCid) { | 1408 if (load->result_cid() != kDynamicCid) { |
| 1413 // Reset value types if guarded_cid was used. | 1409 // Reset value types if guarded_cid was used. |
| 1414 for (Value::Iterator it(load->input_use_list()); | 1410 for (Value::Iterator it(load->input_use_list()); |
| 1415 !it.Done(); | 1411 !it.Done(); |
| 1416 it.Advance()) { | 1412 it.Advance()) { |
| 1417 it.Current()->SetReachingType(NULL); | 1413 it.Current()->SetReachingType(NULL); |
| 1418 } | 1414 } |
| 1419 } | 1415 } |
| 1420 return true; | 1416 return true; |
| 1421 } | 1417 } |
| 1422 | 1418 |
| 1423 | 1419 |
| 1424 bool AotOptimizer::InlineFloat32x4Getter(InstanceCallInstr* call, | 1420 bool AotOptimizer::InlineFloat32x4Getter(InstanceCallInstr* call, |
| 1425 MethodRecognizer::Kind getter) { | 1421 MethodRecognizer::Kind getter) { |
| 1426 if (!ShouldInlineSimd()) { | 1422 if (!ShouldInlineSimd()) { |
| 1427 return false; | 1423 return false; |
| 1428 } | 1424 } |
| 1429 AddCheckClass(call->ArgumentAt(0), | 1425 AddCheckClass(call->ArgumentAt(0), |
| 1430 ICData::ZoneHandle( | 1426 ICData::ZoneHandle( |
| 1431 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), | 1427 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), |
| 1432 call->deopt_id(), | 1428 call->deopt_id(), |
| 1433 call->env(), | 1429 call->env(), |
| 1434 call); | 1430 call); |
| 1435 intptr_t mask = 0; | 1431 intptr_t mask = 0; |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1490 call->deopt_id()); | 1486 call->deopt_id()); |
| 1491 ReplaceCall(call, instr); | 1487 ReplaceCall(call, instr); |
| 1492 return true; | 1488 return true; |
| 1493 } | 1489 } |
| 1494 UNREACHABLE(); | 1490 UNREACHABLE(); |
| 1495 return false; | 1491 return false; |
| 1496 } | 1492 } |
| 1497 | 1493 |
| 1498 | 1494 |
| 1499 bool AotOptimizer::InlineFloat64x2Getter(InstanceCallInstr* call, | 1495 bool AotOptimizer::InlineFloat64x2Getter(InstanceCallInstr* call, |
| 1500 MethodRecognizer::Kind getter) { | 1496 MethodRecognizer::Kind getter) { |
| 1501 if (!ShouldInlineSimd()) { | 1497 if (!ShouldInlineSimd()) { |
| 1502 return false; | 1498 return false; |
| 1503 } | 1499 } |
| 1504 AddCheckClass(call->ArgumentAt(0), | 1500 AddCheckClass(call->ArgumentAt(0), |
| 1505 ICData::ZoneHandle( | 1501 ICData::ZoneHandle( |
| 1506 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), | 1502 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), |
| 1507 call->deopt_id(), | 1503 call->deopt_id(), |
| 1508 call->env(), | 1504 call->env(), |
| 1509 call); | 1505 call); |
| 1510 if ((getter == MethodRecognizer::kFloat64x2GetX) || | 1506 if ((getter == MethodRecognizer::kFloat64x2GetX) || |
| 1511 (getter == MethodRecognizer::kFloat64x2GetY)) { | 1507 (getter == MethodRecognizer::kFloat64x2GetY)) { |
| 1512 Simd64x2ShuffleInstr* instr = new(Z) Simd64x2ShuffleInstr( | 1508 Simd64x2ShuffleInstr* instr = new(Z) Simd64x2ShuffleInstr( |
| 1513 getter, | 1509 getter, |
| 1514 new(Z) Value(call->ArgumentAt(0)), | 1510 new(Z) Value(call->ArgumentAt(0)), |
| 1515 0, | 1511 0, |
| 1516 call->deopt_id()); | 1512 call->deopt_id()); |
| 1517 ReplaceCall(call, instr); | 1513 ReplaceCall(call, instr); |
| 1518 return true; | 1514 return true; |
| 1519 } | 1515 } |
| 1520 UNREACHABLE(); | 1516 UNREACHABLE(); |
| 1521 return false; | 1517 return false; |
| 1522 } | 1518 } |
| 1523 | 1519 |
| 1524 | 1520 |
| 1525 bool AotOptimizer::InlineInt32x4Getter(InstanceCallInstr* call, | 1521 bool AotOptimizer::InlineInt32x4Getter(InstanceCallInstr* call, |
| 1526 MethodRecognizer::Kind getter) { | 1522 MethodRecognizer::Kind getter) { |
| 1527 if (!ShouldInlineSimd()) { | 1523 if (!ShouldInlineSimd()) { |
| 1528 return false; | 1524 return false; |
| 1529 } | 1525 } |
| 1530 AddCheckClass(call->ArgumentAt(0), | 1526 AddCheckClass(call->ArgumentAt(0), |
| 1531 ICData::ZoneHandle( | 1527 ICData::ZoneHandle( |
| 1532 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), | 1528 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), |
| 1533 call->deopt_id(), | 1529 call->deopt_id(), |
| 1534 call->env(), | 1530 call->env(), |
| 1535 call); | 1531 call); |
| 1536 intptr_t mask = 0; | 1532 intptr_t mask = 0; |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1591 getter, | 1587 getter, |
| 1592 new(Z) Value(call->ArgumentAt(0)), | 1588 new(Z) Value(call->ArgumentAt(0)), |
| 1593 call->deopt_id()); | 1589 call->deopt_id()); |
| 1594 ReplaceCall(call, instr); | 1590 ReplaceCall(call, instr); |
| 1595 return true; | 1591 return true; |
| 1596 } | 1592 } |
| 1597 } | 1593 } |
| 1598 | 1594 |
| 1599 | 1595 |
| 1600 bool AotOptimizer::InlineFloat32x4BinaryOp(InstanceCallInstr* call, | 1596 bool AotOptimizer::InlineFloat32x4BinaryOp(InstanceCallInstr* call, |
| 1601 Token::Kind op_kind) { | 1597 Token::Kind op_kind) { |
| 1602 if (!ShouldInlineSimd()) { | 1598 if (!ShouldInlineSimd()) { |
| 1603 return false; | 1599 return false; |
| 1604 } | 1600 } |
| 1605 ASSERT(call->ArgumentCount() == 2); | 1601 ASSERT(call->ArgumentCount() == 2); |
| 1606 Definition* left = call->ArgumentAt(0); | 1602 Definition* left = call->ArgumentAt(0); |
| 1607 Definition* right = call->ArgumentAt(1); | 1603 Definition* right = call->ArgumentAt(1); |
| 1608 // Type check left. | 1604 // Type check left. |
| 1609 AddCheckClass(left, | 1605 AddCheckClass(left, |
| 1610 ICData::ZoneHandle( | 1606 ICData::ZoneHandle( |
| 1611 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), | 1607 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), |
| (...skipping 12 matching lines...) Expand all Loading... |
| 1624 new(Z) BinaryFloat32x4OpInstr( | 1620 new(Z) BinaryFloat32x4OpInstr( |
| 1625 op_kind, new(Z) Value(left), new(Z) Value(right), | 1621 op_kind, new(Z) Value(left), new(Z) Value(right), |
| 1626 call->deopt_id()); | 1622 call->deopt_id()); |
| 1627 ReplaceCall(call, float32x4_bin_op); | 1623 ReplaceCall(call, float32x4_bin_op); |
| 1628 | 1624 |
| 1629 return true; | 1625 return true; |
| 1630 } | 1626 } |
| 1631 | 1627 |
| 1632 | 1628 |
| 1633 bool AotOptimizer::InlineInt32x4BinaryOp(InstanceCallInstr* call, | 1629 bool AotOptimizer::InlineInt32x4BinaryOp(InstanceCallInstr* call, |
| 1634 Token::Kind op_kind) { | 1630 Token::Kind op_kind) { |
| 1635 if (!ShouldInlineSimd()) { | 1631 if (!ShouldInlineSimd()) { |
| 1636 return false; | 1632 return false; |
| 1637 } | 1633 } |
| 1638 ASSERT(call->ArgumentCount() == 2); | 1634 ASSERT(call->ArgumentCount() == 2); |
| 1639 Definition* left = call->ArgumentAt(0); | 1635 Definition* left = call->ArgumentAt(0); |
| 1640 Definition* right = call->ArgumentAt(1); | 1636 Definition* right = call->ArgumentAt(1); |
| 1641 // Type check left. | 1637 // Type check left. |
| 1642 AddCheckClass(left, | 1638 AddCheckClass(left, |
| 1643 ICData::ZoneHandle( | 1639 ICData::ZoneHandle( |
| 1644 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), | 1640 Z, call->ic_data()->AsUnaryClassChecksForArgNr(0)), |
| (...skipping 11 matching lines...) Expand all Loading... |
| 1656 BinaryInt32x4OpInstr* int32x4_bin_op = | 1652 BinaryInt32x4OpInstr* int32x4_bin_op = |
| 1657 new(Z) BinaryInt32x4OpInstr( | 1653 new(Z) BinaryInt32x4OpInstr( |
| 1658 op_kind, new(Z) Value(left), new(Z) Value(right), | 1654 op_kind, new(Z) Value(left), new(Z) Value(right), |
| 1659 call->deopt_id()); | 1655 call->deopt_id()); |
| 1660 ReplaceCall(call, int32x4_bin_op); | 1656 ReplaceCall(call, int32x4_bin_op); |
| 1661 return true; | 1657 return true; |
| 1662 } | 1658 } |
| 1663 | 1659 |
| 1664 | 1660 |
| 1665 bool AotOptimizer::InlineFloat64x2BinaryOp(InstanceCallInstr* call, | 1661 bool AotOptimizer::InlineFloat64x2BinaryOp(InstanceCallInstr* call, |
| 1666 Token::Kind op_kind) { | 1662 Token::Kind op_kind) { |
| 1667 if (!ShouldInlineSimd()) { | 1663 if (!ShouldInlineSimd()) { |
| 1668 return false; | 1664 return false; |
| 1669 } | 1665 } |
| 1670 ASSERT(call->ArgumentCount() == 2); | 1666 ASSERT(call->ArgumentCount() == 2); |
| 1671 Definition* left = call->ArgumentAt(0); | 1667 Definition* left = call->ArgumentAt(0); |
| 1672 Definition* right = call->ArgumentAt(1); | 1668 Definition* right = call->ArgumentAt(1); |
| 1673 // Type check left. | 1669 // Type check left. |
| 1674 AddCheckClass(left, | 1670 AddCheckClass(left, |
| 1675 ICData::ZoneHandle( | 1671 ICData::ZoneHandle( |
| 1676 call->ic_data()->AsUnaryClassChecksForArgNr(0)), | 1672 call->ic_data()->AsUnaryClassChecksForArgNr(0)), |
| (...skipping 11 matching lines...) Expand all Loading... |
| 1688 BinaryFloat64x2OpInstr* float64x2_bin_op = | 1684 BinaryFloat64x2OpInstr* float64x2_bin_op = |
| 1689 new(Z) BinaryFloat64x2OpInstr( | 1685 new(Z) BinaryFloat64x2OpInstr( |
| 1690 op_kind, new(Z) Value(left), new(Z) Value(right), | 1686 op_kind, new(Z) Value(left), new(Z) Value(right), |
| 1691 call->deopt_id()); | 1687 call->deopt_id()); |
| 1692 ReplaceCall(call, float64x2_bin_op); | 1688 ReplaceCall(call, float64x2_bin_op); |
| 1693 return true; | 1689 return true; |
| 1694 } | 1690 } |
| 1695 | 1691 |
| 1696 | 1692 |
| 1697 // Only unique implicit instance getters can be currently handled. | 1693 // Only unique implicit instance getters can be currently handled. |
| 1698 // Returns false if 'allow_check' is false and a check is needed. | 1694 bool AotOptimizer::TryInlineInstanceGetter(InstanceCallInstr* call) { |
| 1699 bool AotOptimizer::TryInlineInstanceGetter(InstanceCallInstr* call, | |
| 1700 bool allow_check) { | |
| 1701 ASSERT(call->HasICData()); | 1695 ASSERT(call->HasICData()); |
| 1702 const ICData& ic_data = *call->ic_data(); | 1696 const ICData& ic_data = *call->ic_data(); |
| 1703 if (ic_data.NumberOfUsedChecks() == 0) { | 1697 if (ic_data.NumberOfUsedChecks() == 0) { |
| 1704 // No type feedback collected. | 1698 // No type feedback collected. |
| 1705 return false; | 1699 return false; |
| 1706 } | 1700 } |
| 1707 | 1701 |
| 1708 if (!ic_data.HasOneTarget()) { | 1702 if (!ic_data.HasOneTarget()) { |
| 1709 // Polymorphic sites are inlined like normal methods by conventional | 1703 // Polymorphic sites are inlined like normal methods by conventional |
| 1710 // inlining in FlowGraphInliner. | 1704 // inlining in FlowGraphInliner. |
| 1711 return false; | 1705 return false; |
| 1712 } | 1706 } |
| 1713 | 1707 |
| 1714 const Function& target = Function::Handle(Z, ic_data.GetTargetAt(0)); | 1708 const Function& target = Function::Handle(Z, ic_data.GetTargetAt(0)); |
| 1715 if (target.kind() != RawFunction::kImplicitGetter) { | 1709 if (target.kind() != RawFunction::kImplicitGetter) { |
| 1716 // Non-implicit getters are inlined like normal methods by conventional | 1710 // Non-implicit getters are inlined like normal methods by conventional |
| 1717 // inlining in FlowGraphInliner. | 1711 // inlining in FlowGraphInliner. |
| 1718 return false; | 1712 return false; |
| 1719 } | 1713 } |
| 1720 return InlineImplicitInstanceGetter(call, allow_check); | 1714 return InlineImplicitInstanceGetter(call); |
| 1721 } | 1715 } |
| 1722 | 1716 |
| 1723 | 1717 |
| 1724 bool AotOptimizer::TryReplaceInstanceCallWithInline( | 1718 bool AotOptimizer::TryReplaceInstanceCallWithInline( |
| 1725 InstanceCallInstr* call) { | 1719 InstanceCallInstr* call) { |
| 1726 Function& target = Function::Handle(Z); | 1720 Function& target = Function::Handle(Z); |
| 1727 GrowableArray<intptr_t> class_ids; | 1721 GrowableArray<intptr_t> class_ids; |
| 1728 call->ic_data()->GetCheckAt(0, &class_ids, &target); | 1722 call->ic_data()->GetCheckAt(0, &class_ids, &target); |
| 1729 const intptr_t receiver_cid = class_ids[0]; | 1723 const intptr_t receiver_cid = class_ids[0]; |
| 1730 | 1724 |
| (...skipping 696 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2427 if (Token::IsTypeTestOperator(op_kind)) { | 2421 if (Token::IsTypeTestOperator(op_kind)) { |
| 2428 ReplaceWithInstanceOf(instr); | 2422 ReplaceWithInstanceOf(instr); |
| 2429 return; | 2423 return; |
| 2430 } | 2424 } |
| 2431 if (Token::IsTypeCastOperator(op_kind)) { | 2425 if (Token::IsTypeCastOperator(op_kind)) { |
| 2432 ReplaceWithTypeCast(instr); | 2426 ReplaceWithTypeCast(instr); |
| 2433 return; | 2427 return; |
| 2434 } | 2428 } |
| 2435 | 2429 |
| 2436 if ((op_kind == Token::kGET) && | 2430 if ((op_kind == Token::kGET) && |
| 2437 TryInlineInstanceGetter(instr, false /* no checks allowed */)) { | 2431 TryInlineInstanceGetter(instr)) { |
| 2438 return; | 2432 return; |
| 2439 } | 2433 } |
| 2440 const ICData& unary_checks = | 2434 const ICData& unary_checks = |
| 2441 ICData::ZoneHandle(Z, instr->ic_data()->AsUnaryClassChecks()); | 2435 ICData::ZoneHandle(Z, instr->ic_data()->AsUnaryClassChecks()); |
| 2442 if ((unary_checks.NumberOfChecks() > 0) && | 2436 if ((unary_checks.NumberOfChecks() > 0) && |
| 2443 (op_kind == Token::kSET) && | 2437 (op_kind == Token::kSET) && |
| 2444 TryInlineInstanceSetter(instr, unary_checks, false /* no checks */)) { | 2438 TryInlineInstanceSetter(instr, unary_checks)) { |
| 2445 return; | 2439 return; |
| 2446 } | 2440 } |
| 2447 | 2441 |
| 2448 if (use_speculative_inlining_ && | 2442 if (use_speculative_inlining_ && |
| 2449 !IsBlackListedForInlining(instr->deopt_id()) && | 2443 !IsBlackListedForInlining(instr->deopt_id()) && |
| 2450 (unary_checks.NumberOfChecks() > 0)) { | 2444 (unary_checks.NumberOfChecks() > 0)) { |
| 2451 if ((op_kind == Token::kINDEX) && TryReplaceWithIndexedOp(instr)) { | 2445 if ((op_kind == Token::kINDEX) && TryReplaceWithIndexedOp(instr)) { |
| 2452 return; | 2446 return; |
| 2453 } | 2447 } |
| 2454 if ((op_kind == Token::kASSIGN_INDEX) && TryReplaceWithIndexedOp(instr)) { | 2448 if ((op_kind == Token::kASSIGN_INDEX) && TryReplaceWithIndexedOp(instr)) { |
| (...skipping 306 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2761 void AotOptimizer::VisitLoadCodeUnits(LoadCodeUnitsInstr* instr) { | 2755 void AotOptimizer::VisitLoadCodeUnits(LoadCodeUnitsInstr* instr) { |
| 2762 // TODO(zerny): Use kUnboxedUint32 once it is fully supported/optimized. | 2756 // TODO(zerny): Use kUnboxedUint32 once it is fully supported/optimized. |
| 2763 #if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_ARM) | 2757 #if defined(TARGET_ARCH_IA32) || defined(TARGET_ARCH_ARM) |
| 2764 if (!instr->can_pack_into_smi()) | 2758 if (!instr->can_pack_into_smi()) |
| 2765 instr->set_representation(kUnboxedMint); | 2759 instr->set_representation(kUnboxedMint); |
| 2766 #endif | 2760 #endif |
| 2767 } | 2761 } |
| 2768 | 2762 |
| 2769 | 2763 |
| 2770 bool AotOptimizer::TryInlineInstanceSetter(InstanceCallInstr* instr, | 2764 bool AotOptimizer::TryInlineInstanceSetter(InstanceCallInstr* instr, |
| 2771 const ICData& unary_ic_data, | 2765 const ICData& unary_ic_data) { |
| 2772 bool allow_checks) { | |
| 2773 ASSERT((unary_ic_data.NumberOfChecks() > 0) && | 2766 ASSERT((unary_ic_data.NumberOfChecks() > 0) && |
| 2774 (unary_ic_data.NumArgsTested() == 1)); | 2767 (unary_ic_data.NumArgsTested() == 1)); |
| 2775 if (I->flags().type_checks()) { | 2768 if (I->flags().type_checks()) { |
| 2776 // Checked mode setters are inlined like normal methods by conventional | 2769 // Checked mode setters are inlined like normal methods by conventional |
| 2777 // inlining. | 2770 // inlining. |
| 2778 return false; | 2771 return false; |
| 2779 } | 2772 } |
| 2780 | 2773 |
| 2781 ASSERT(instr->HasICData()); | 2774 ASSERT(instr->HasICData()); |
| 2782 if (unary_ic_data.NumberOfChecks() == 0) { | 2775 if (unary_ic_data.NumberOfChecks() == 0) { |
| (...skipping 13 matching lines...) Expand all Loading... |
| 2796 return false; | 2789 return false; |
| 2797 } | 2790 } |
| 2798 // Inline implicit instance setter. | 2791 // Inline implicit instance setter. |
| 2799 const String& field_name = | 2792 const String& field_name = |
| 2800 String::Handle(Z, Field::NameFromSetter(instr->function_name())); | 2793 String::Handle(Z, Field::NameFromSetter(instr->function_name())); |
| 2801 const Field& field = | 2794 const Field& field = |
| 2802 Field::ZoneHandle(Z, GetField(class_id, field_name)); | 2795 Field::ZoneHandle(Z, GetField(class_id, field_name)); |
| 2803 ASSERT(!field.IsNull()); | 2796 ASSERT(!field.IsNull()); |
| 2804 | 2797 |
| 2805 if (InstanceCallNeedsClassCheck(instr, RawFunction::kImplicitSetter)) { | 2798 if (InstanceCallNeedsClassCheck(instr, RawFunction::kImplicitSetter)) { |
| 2806 if (!allow_checks) { | 2799 return false; |
| 2807 return false; | |
| 2808 } | |
| 2809 AddReceiverCheck(instr); | |
| 2810 } | |
| 2811 if (field.guarded_cid() != kDynamicCid) { | |
| 2812 if (!allow_checks) { | |
| 2813 return false; | |
| 2814 } | |
| 2815 InsertBefore(instr, | |
| 2816 new(Z) GuardFieldClassInstr( | |
| 2817 new(Z) Value(instr->ArgumentAt(1)), | |
| 2818 field, | |
| 2819 instr->deopt_id()), | |
| 2820 instr->env(), | |
| 2821 FlowGraph::kEffect); | |
| 2822 } | |
| 2823 | |
| 2824 if (field.needs_length_check()) { | |
| 2825 if (!allow_checks) { | |
| 2826 return false; | |
| 2827 } | |
| 2828 InsertBefore(instr, | |
| 2829 new(Z) GuardFieldLengthInstr( | |
| 2830 new(Z) Value(instr->ArgumentAt(1)), | |
| 2831 field, | |
| 2832 instr->deopt_id()), | |
| 2833 instr->env(), | |
| 2834 FlowGraph::kEffect); | |
| 2835 } | 2800 } |
| 2836 | 2801 |
| 2837 // Field guard was detached. | 2802 // Field guard was detached. |
| 2838 StoreInstanceFieldInstr* store = new(Z) StoreInstanceFieldInstr( | 2803 StoreInstanceFieldInstr* store = new(Z) StoreInstanceFieldInstr( |
| 2839 field, | 2804 field, |
| 2840 new(Z) Value(instr->ArgumentAt(0)), | 2805 new(Z) Value(instr->ArgumentAt(0)), |
| 2841 new(Z) Value(instr->ArgumentAt(1)), | 2806 new(Z) Value(instr->ArgumentAt(1)), |
| 2842 kEmitStoreBarrier, | 2807 kEmitStoreBarrier, |
| 2843 instr->token_pos()); | 2808 instr->token_pos()); |
| 2844 | 2809 |
| 2845 // No unboxed stores in precompiled code. | 2810 // No unboxed stores in precompiled code. |
| 2846 ASSERT(!store->IsUnboxedStore()); | 2811 ASSERT(!store->IsUnboxedStore()); |
| 2847 | 2812 |
| 2848 // Discard the environment from the original instruction because the store | 2813 // Discard the environment from the original instruction because the store |
| 2849 // can't deoptimize. | 2814 // can't deoptimize. |
| 2850 instr->RemoveEnvironment(); | 2815 instr->RemoveEnvironment(); |
| 2851 ReplaceCall(instr, store); | 2816 ReplaceCall(instr, store); |
| 2852 return true; | 2817 return true; |
| 2853 } | 2818 } |
| 2854 | 2819 |
| 2855 | 2820 |
| 2856 } // namespace dart | 2821 } // namespace dart |
| OLD | NEW |