| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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/globals.h" // Needed here to get TARGET_ARCH_ARM64. | 5 #include "vm/globals.h" // Needed here to get TARGET_ARCH_ARM64. |
| 6 #if defined(TARGET_ARCH_ARM64) | 6 #if defined(TARGET_ARCH_ARM64) |
| 7 | 7 |
| 8 #include "vm/flow_graph_compiler.h" | 8 #include "vm/flow_graph_compiler.h" |
| 9 | 9 |
| 10 #include "vm/ast_printer.h" | 10 #include "vm/ast_printer.h" |
| 11 #include "vm/compiler.h" | 11 #include "vm/compiler.h" |
| 12 #include "vm/cpu.h" | 12 #include "vm/cpu.h" |
| 13 #include "vm/dart_entry.h" | 13 #include "vm/dart_entry.h" |
| 14 #include "vm/deopt_instructions.h" | 14 #include "vm/deopt_instructions.h" |
| 15 #include "vm/il_printer.h" | 15 #include "vm/il_printer.h" |
| 16 #include "vm/locations.h" | 16 #include "vm/locations.h" |
| 17 #include "vm/object_store.h" | 17 #include "vm/object_store.h" |
| 18 #include "vm/parser.h" | 18 #include "vm/parser.h" |
| 19 #include "vm/stack_frame.h" | 19 #include "vm/stack_frame.h" |
| 20 #include "vm/stub_code.h" | 20 #include "vm/stub_code.h" |
| 21 #include "vm/symbols.h" | 21 #include "vm/symbols.h" |
| 22 | 22 |
| 23 namespace dart { | 23 namespace dart { |
| 24 | 24 |
| 25 DEFINE_FLAG(bool, trap_on_deoptimization, false, "Trap on deoptimization."); |
| 25 DECLARE_FLAG(int, optimization_counter_threshold); | 26 DECLARE_FLAG(int, optimization_counter_threshold); |
| 26 DECLARE_FLAG(int, reoptimization_counter_threshold); | 27 DECLARE_FLAG(int, reoptimization_counter_threshold); |
| 27 DECLARE_FLAG(bool, eliminate_type_checks); | 28 DECLARE_FLAG(bool, eliminate_type_checks); |
| 28 | 29 |
| 29 FlowGraphCompiler::~FlowGraphCompiler() { | 30 FlowGraphCompiler::~FlowGraphCompiler() { |
| 30 // BlockInfos are zone-allocated, so their destructors are not called. | 31 // BlockInfos are zone-allocated, so their destructors are not called. |
| 31 // Verify the labels explicitly here. | 32 // Verify the labels explicitly here. |
| 32 for (int i = 0; i < block_info_.length(); ++i) { | 33 for (int i = 0; i < block_info_.length(); ++i) { |
| 33 ASSERT(!block_info_[i]->jump_label()->IsLinked()); | 34 ASSERT(!block_info_[i]->jump_label()->IsLinked()); |
| 34 } | 35 } |
| (...skipping 11 matching lines...) Expand all Loading... |
| 46 | 47 |
| 47 | 48 |
| 48 bool FlowGraphCompiler::SupportsSinCos() { | 49 bool FlowGraphCompiler::SupportsSinCos() { |
| 49 return false; | 50 return false; |
| 50 } | 51 } |
| 51 | 52 |
| 52 | 53 |
| 53 RawDeoptInfo* CompilerDeoptInfo::CreateDeoptInfo(FlowGraphCompiler* compiler, | 54 RawDeoptInfo* CompilerDeoptInfo::CreateDeoptInfo(FlowGraphCompiler* compiler, |
| 54 DeoptInfoBuilder* builder, | 55 DeoptInfoBuilder* builder, |
| 55 const Array& deopt_table) { | 56 const Array& deopt_table) { |
| 56 UNIMPLEMENTED(); | 57 if (deopt_env_ == NULL) { |
| 57 return NULL; | 58 return DeoptInfo::null(); |
| 59 } |
| 60 |
| 61 intptr_t stack_height = compiler->StackSize(); |
| 62 AllocateIncomingParametersRecursive(deopt_env_, &stack_height); |
| 63 |
| 64 intptr_t slot_ix = 0; |
| 65 Environment* current = deopt_env_; |
| 66 |
| 67 // Emit all kMaterializeObject instructions describing objects to be |
| 68 // materialized on the deoptimization as a prefix to the deoptimization info. |
| 69 EmitMaterializations(deopt_env_, builder); |
| 70 |
| 71 // The real frame starts here. |
| 72 builder->MarkFrameStart(); |
| 73 |
| 74 // Current PP, FP, and PC. |
| 75 builder->AddPp(current->code(), slot_ix++); |
| 76 builder->AddPcMarker(Code::Handle(), slot_ix++); |
| 77 builder->AddCallerFp(slot_ix++); |
| 78 builder->AddReturnAddress(current->code(), deopt_id(), slot_ix++); |
| 79 |
| 80 // Emit all values that are needed for materialization as a part of the |
| 81 // expression stack for the bottom-most frame. This guarantees that GC |
| 82 // will be able to find them during materialization. |
| 83 slot_ix = builder->EmitMaterializationArguments(slot_ix); |
| 84 |
| 85 // For the innermost environment, set outgoing arguments and the locals. |
| 86 for (intptr_t i = current->Length() - 1; |
| 87 i >= current->fixed_parameter_count(); |
| 88 i--) { |
| 89 builder->AddCopy(current->ValueAt(i), current->LocationAt(i), slot_ix++); |
| 90 } |
| 91 |
| 92 Environment* previous = current; |
| 93 current = current->outer(); |
| 94 while (current != NULL) { |
| 95 // PP, FP, and PC. |
| 96 builder->AddPp(current->code(), slot_ix++); |
| 97 builder->AddPcMarker(previous->code(), slot_ix++); |
| 98 builder->AddCallerFp(slot_ix++); |
| 99 |
| 100 // For any outer environment the deopt id is that of the call instruction |
| 101 // which is recorded in the outer environment. |
| 102 builder->AddReturnAddress(current->code(), |
| 103 Isolate::ToDeoptAfter(current->deopt_id()), |
| 104 slot_ix++); |
| 105 |
| 106 // The values of outgoing arguments can be changed from the inlined call so |
| 107 // we must read them from the previous environment. |
| 108 for (intptr_t i = previous->fixed_parameter_count() - 1; i >= 0; i--) { |
| 109 builder->AddCopy(previous->ValueAt(i), |
| 110 previous->LocationAt(i), |
| 111 slot_ix++); |
| 112 } |
| 113 |
| 114 // Set the locals, note that outgoing arguments are not in the environment. |
| 115 for (intptr_t i = current->Length() - 1; |
| 116 i >= current->fixed_parameter_count(); |
| 117 i--) { |
| 118 builder->AddCopy(current->ValueAt(i), |
| 119 current->LocationAt(i), |
| 120 slot_ix++); |
| 121 } |
| 122 |
| 123 // Iterate on the outer environment. |
| 124 previous = current; |
| 125 current = current->outer(); |
| 126 } |
| 127 // The previous pointer is now the outermost environment. |
| 128 ASSERT(previous != NULL); |
| 129 |
| 130 // For the outermost environment, set caller PC, caller PP, and caller FP. |
| 131 builder->AddCallerPp(slot_ix++); |
| 132 // PC marker. |
| 133 builder->AddPcMarker(previous->code(), slot_ix++); |
| 134 builder->AddCallerFp(slot_ix++); |
| 135 builder->AddCallerPc(slot_ix++); |
| 136 |
| 137 // For the outermost environment, set the incoming arguments. |
| 138 for (intptr_t i = previous->fixed_parameter_count() - 1; i >= 0; i--) { |
| 139 builder->AddCopy(previous->ValueAt(i), previous->LocationAt(i), slot_ix++); |
| 140 } |
| 141 |
| 142 const DeoptInfo& deopt_info = |
| 143 DeoptInfo::Handle(builder->CreateDeoptInfo(deopt_table)); |
| 144 return deopt_info.raw(); |
| 58 } | 145 } |
| 59 | 146 |
| 60 | 147 |
| 61 void CompilerDeoptInfoWithStub::GenerateCode(FlowGraphCompiler* compiler, | 148 void CompilerDeoptInfoWithStub::GenerateCode(FlowGraphCompiler* compiler, |
| 62 intptr_t stub_ix) { | 149 intptr_t stub_ix) { |
| 63 UNIMPLEMENTED(); | 150 // Calls do not need stubs, they share a deoptimization trampoline. |
| 151 ASSERT(reason() != ICData::kDeoptAtCall); |
| 152 Assembler* assem = compiler->assembler(); |
| 153 #define __ assem-> |
| 154 __ Comment("Deopt stub for id %" Pd "", deopt_id()); |
| 155 __ Bind(entry_label()); |
| 156 if (FLAG_trap_on_deoptimization) { |
| 157 __ hlt(0); |
| 158 } |
| 159 |
| 160 ASSERT(deopt_env() != NULL); |
| 161 |
| 162 __ BranchLink(&StubCode::DeoptimizeLabel(), PP); |
| 163 set_pc_offset(assem->CodeSize()); |
| 164 #undef __ |
| 64 } | 165 } |
| 65 | 166 |
| 66 | 167 |
| 67 #define __ assembler()-> | 168 #define __ assembler()-> |
| 68 | 169 |
| 69 | 170 |
| 70 // Fall through if bool_register contains null. | 171 // Fall through if bool_register contains null. |
| 71 void FlowGraphCompiler::GenerateBoolToJump(Register bool_register, | 172 void FlowGraphCompiler::GenerateBoolToJump(Register bool_register, |
| 72 Label* is_true, | 173 Label* is_true, |
| 73 Label* is_false) { | 174 Label* is_false) { |
| (...skipping 351 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 425 return SubtypeTestCache::null(); | 526 return SubtypeTestCache::null(); |
| 426 } | 527 } |
| 427 } | 528 } |
| 428 return GenerateUninstantiatedTypeTest(token_pos, | 529 return GenerateUninstantiatedTypeTest(token_pos, |
| 429 type, | 530 type, |
| 430 is_instance_lbl, | 531 is_instance_lbl, |
| 431 is_not_instance_lbl); | 532 is_not_instance_lbl); |
| 432 } | 533 } |
| 433 | 534 |
| 434 | 535 |
| 536 // If instanceof type test cannot be performed successfully at compile time and |
| 537 // therefore eliminated, optimize it by adding inlined tests for: |
| 538 // - NULL -> return false. |
| 539 // - Smi -> compile time subtype check (only if dst class is not parameterized). |
| 540 // - Class equality (only if class is not parameterized). |
| 541 // Inputs: |
| 542 // - R0: object. |
| 543 // - R1: instantiator type arguments or raw_null. |
| 544 // - R2: instantiator or raw_null. |
| 545 // Returns: |
| 546 // - true or false in R0. |
| 435 void FlowGraphCompiler::GenerateInstanceOf(intptr_t token_pos, | 547 void FlowGraphCompiler::GenerateInstanceOf(intptr_t token_pos, |
| 436 intptr_t deopt_id, | 548 intptr_t deopt_id, |
| 437 const AbstractType& type, | 549 const AbstractType& type, |
| 438 bool negate_result, | 550 bool negate_result, |
| 439 LocationSummary* locs) { | 551 LocationSummary* locs) { |
| 440 UNIMPLEMENTED(); | 552 ASSERT(type.IsFinalized() && !type.IsMalformed() && !type.IsMalbounded()); |
| 553 |
| 554 // Preserve instantiator (R2) and its type arguments (R1). |
| 555 __ Push(R2); |
| 556 __ Push(R1); |
| 557 |
| 558 Label is_instance, is_not_instance; |
| 559 // If type is instantiated and non-parameterized, we can inline code |
| 560 // checking whether the tested instance is a Smi. |
| 561 if (type.IsInstantiated()) { |
| 562 // A null object is only an instance of Object and dynamic, which has |
| 563 // already been checked above (if the type is instantiated). So we can |
| 564 // return false here if the instance is null (and if the type is |
| 565 // instantiated). |
| 566 // We can only inline this null check if the type is instantiated at compile |
| 567 // time, since an uninstantiated type at compile time could be Object or |
| 568 // dynamic at run time. |
| 569 __ CompareObject(R0, Object::null_object(), PP); |
| 570 __ b(&is_not_instance, EQ); |
| 571 } |
| 572 |
| 573 // Generate inline instanceof test. |
| 574 SubtypeTestCache& test_cache = SubtypeTestCache::ZoneHandle(); |
| 575 test_cache = GenerateInlineInstanceof(token_pos, type, |
| 576 &is_instance, &is_not_instance); |
| 577 |
| 578 // test_cache is null if there is no fall-through. |
| 579 Label done; |
| 580 if (!test_cache.IsNull()) { |
| 581 // Generate runtime call. |
| 582 // Load instantiator (R2) and its type arguments (R1). |
| 583 __ ldr(R1, Address(SP, 0 * kWordSize)); |
| 584 __ ldr(R2, Address(SP, 1 * kWordSize)); |
| 585 __ PushObject(Object::ZoneHandle(), PP); // Make room for the result. |
| 586 __ Push(R0); // Push the instance. |
| 587 __ PushObject(type, PP); // Push the type. |
| 588 // Push instantiator (R2) and its type arguments (R1). |
| 589 __ Push(R2); |
| 590 __ Push(R1); |
| 591 __ LoadObject(R0, test_cache, PP); |
| 592 __ Push(R0); |
| 593 GenerateRuntimeCall(token_pos, deopt_id, kInstanceofRuntimeEntry, 5, locs); |
| 594 // Pop the parameters supplied to the runtime entry. The result of the |
| 595 // instanceof runtime call will be left as the result of the operation. |
| 596 __ Drop(5); |
| 597 if (negate_result) { |
| 598 __ Pop(R1); |
| 599 __ LoadObject(R0, Bool::True(), PP); |
| 600 __ CompareRegisters(R1, R0); |
| 601 __ b(&done, NE); |
| 602 __ LoadObject(R0, Bool::False(), PP); |
| 603 } else { |
| 604 __ Pop(R0); |
| 605 } |
| 606 __ b(&done); |
| 607 } |
| 608 __ Bind(&is_not_instance); |
| 609 __ LoadObject(R0, Bool::Get(negate_result), PP); |
| 610 __ b(&done); |
| 611 |
| 612 __ Bind(&is_instance); |
| 613 __ LoadObject(R0, Bool::Get(!negate_result), PP); |
| 614 __ Bind(&done); |
| 615 // Remove instantiator (R2) and its type arguments (R1). |
| 616 __ Drop(2); |
| 441 } | 617 } |
| 442 | 618 |
| 443 | 619 |
| 444 // Optimize assignable type check by adding inlined tests for: | 620 // Optimize assignable type check by adding inlined tests for: |
| 445 // - NULL -> return NULL. | 621 // - NULL -> return NULL. |
| 446 // - Smi -> compile time subtype check (only if dst class is not parameterized). | 622 // - Smi -> compile time subtype check (only if dst class is not parameterized). |
| 447 // - Class equality (only if class is not parameterized). | 623 // - Class equality (only if class is not parameterized). |
| 448 // Inputs: | 624 // Inputs: |
| 449 // - R0: instance being type checked. | 625 // - R0: instance being type checked. |
| 450 // - R1: instantiator type arguments or raw_null. | 626 // - R1: instantiator type arguments or raw_null. |
| (...skipping 299 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 750 __ b(&null_args_loop_condition); | 926 __ b(&null_args_loop_condition); |
| 751 __ Bind(&null_args_loop); | 927 __ Bind(&null_args_loop); |
| 752 __ str(TMP, original_argument_addr); | 928 __ str(TMP, original_argument_addr); |
| 753 __ Bind(&null_args_loop_condition); | 929 __ Bind(&null_args_loop_condition); |
| 754 __ subs(R8, R8, Operand(1)); | 930 __ subs(R8, R8, Operand(1)); |
| 755 __ b(&null_args_loop, PL); | 931 __ b(&null_args_loop, PL); |
| 756 } | 932 } |
| 757 | 933 |
| 758 | 934 |
| 759 void FlowGraphCompiler::GenerateInlinedGetter(intptr_t offset) { | 935 void FlowGraphCompiler::GenerateInlinedGetter(intptr_t offset) { |
| 760 UNIMPLEMENTED(); | 936 // LR: return address. |
| 937 // SP: receiver. |
| 938 // Sequence node has one return node, its input is load field node. |
| 939 __ Comment("Inlined Getter"); |
| 940 __ LoadFromOffset(R0, SP, 0 * kWordSize); |
| 941 __ LoadFromOffset(R0, R0, offset - kHeapObjectTag); |
| 942 __ ret(); |
| 761 } | 943 } |
| 762 | 944 |
| 763 | 945 |
| 764 void FlowGraphCompiler::GenerateInlinedSetter(intptr_t offset) { | 946 void FlowGraphCompiler::GenerateInlinedSetter(intptr_t offset) { |
| 765 UNIMPLEMENTED(); | 947 UNIMPLEMENTED(); |
| 766 } | 948 } |
| 767 | 949 |
| 768 | 950 |
| 769 void FlowGraphCompiler::EmitFrameEntry() { | 951 void FlowGraphCompiler::EmitFrameEntry() { |
| 770 const Function& function = parsed_function().function(); | 952 const Function& function = parsed_function().function(); |
| (...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 909 | 1091 |
| 910 VisitBlocks(); | 1092 VisitBlocks(); |
| 911 | 1093 |
| 912 __ hlt(0); | 1094 __ hlt(0); |
| 913 GenerateDeferredCode(); | 1095 GenerateDeferredCode(); |
| 914 // Emit function patching code. This will be swapped with the first 3 | 1096 // Emit function patching code. This will be swapped with the first 3 |
| 915 // instructions at entry point. | 1097 // instructions at entry point. |
| 916 AddCurrentDescriptor(PcDescriptors::kPatchCode, | 1098 AddCurrentDescriptor(PcDescriptors::kPatchCode, |
| 917 Isolate::kNoDeoptId, | 1099 Isolate::kNoDeoptId, |
| 918 0); // No token position. | 1100 0); // No token position. |
| 919 // This is patched up to a point in FrameEntry where the PP for the | 1101 __ BranchFixed(&StubCode::FixCallersTargetLabel()); |
| 920 // current function is in R13 instead of PP. | |
| 921 __ BranchPatchable(&StubCode::FixCallersTargetLabel(), R13); | |
| 922 | 1102 |
| 923 AddCurrentDescriptor(PcDescriptors::kLazyDeoptJump, | 1103 AddCurrentDescriptor(PcDescriptors::kLazyDeoptJump, |
| 924 Isolate::kNoDeoptId, | 1104 Isolate::kNoDeoptId, |
| 925 0); // No token position. | 1105 0); // No token position. |
| 926 // TODO(zra): Can I use a normal BranchPatchable here? Probably have to change | 1106 // TODO(zra): Can I use a normal BranchPatchable here? Probably have to change |
| 927 // the CodePatcher. | 1107 // the CodePatcher. |
| 928 __ BranchFixed(&StubCode::DeoptimizeLazyLabel()); | 1108 __ BranchFixed(&StubCode::DeoptimizeLazyLabel()); |
| 929 } | 1109 } |
| 930 | 1110 |
| 931 | 1111 |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 999 } | 1179 } |
| 1000 | 1180 |
| 1001 | 1181 |
| 1002 void FlowGraphCompiler::EmitOptimizedInstanceCall( | 1182 void FlowGraphCompiler::EmitOptimizedInstanceCall( |
| 1003 ExternalLabel* target_label, | 1183 ExternalLabel* target_label, |
| 1004 const ICData& ic_data, | 1184 const ICData& ic_data, |
| 1005 intptr_t argument_count, | 1185 intptr_t argument_count, |
| 1006 intptr_t deopt_id, | 1186 intptr_t deopt_id, |
| 1007 intptr_t token_pos, | 1187 intptr_t token_pos, |
| 1008 LocationSummary* locs) { | 1188 LocationSummary* locs) { |
| 1009 UNIMPLEMENTED(); | 1189 ASSERT(Array::Handle(ic_data.arguments_descriptor()).Length() > 0); |
| 1190 // Each ICData propagated from unoptimized to optimized code contains the |
| 1191 // function that corresponds to the Dart function of that IC call. Due |
| 1192 // to inlining in optimized code, that function may not correspond to the |
| 1193 // top-level function (parsed_function().function()) which could be |
| 1194 // reoptimized and which counter needs to be incremented. |
| 1195 // Pass the function explicitly, it is used in IC stub. |
| 1196 |
| 1197 __ LoadObject(R6, parsed_function().function(), PP); |
| 1198 __ LoadObject(R5, ic_data, PP); |
| 1199 GenerateDartCall(deopt_id, |
| 1200 token_pos, |
| 1201 target_label, |
| 1202 PcDescriptors::kIcCall, |
| 1203 locs); |
| 1204 __ Drop(argument_count); |
| 1010 } | 1205 } |
| 1011 | 1206 |
| 1012 | 1207 |
| 1013 void FlowGraphCompiler::EmitInstanceCall(ExternalLabel* target_label, | 1208 void FlowGraphCompiler::EmitInstanceCall(ExternalLabel* target_label, |
| 1014 const ICData& ic_data, | 1209 const ICData& ic_data, |
| 1015 intptr_t argument_count, | 1210 intptr_t argument_count, |
| 1016 intptr_t deopt_id, | 1211 intptr_t deopt_id, |
| 1017 intptr_t token_pos, | 1212 intptr_t token_pos, |
| 1018 LocationSummary* locs) { | 1213 LocationSummary* locs) { |
| 1019 ASSERT(Array::Handle(ic_data.arguments_descriptor()).Length() > 0); | 1214 ASSERT(Array::Handle(ic_data.arguments_descriptor()).Length() > 0); |
| 1020 __ LoadObject(R5, ic_data, PP); | 1215 __ LoadObject(R5, ic_data, PP); |
| 1021 GenerateDartCall(deopt_id, | 1216 GenerateDartCall(deopt_id, |
| 1022 token_pos, | 1217 token_pos, |
| 1023 target_label, | 1218 target_label, |
| 1024 PcDescriptors::kIcCall, | 1219 PcDescriptors::kIcCall, |
| 1025 locs); | 1220 locs); |
| 1026 __ Drop(argument_count); | 1221 __ Drop(argument_count); |
| 1027 } | 1222 } |
| 1028 | 1223 |
| 1029 | 1224 |
| 1030 void FlowGraphCompiler::EmitMegamorphicInstanceCall( | 1225 void FlowGraphCompiler::EmitMegamorphicInstanceCall( |
| 1031 const ICData& ic_data, | 1226 const ICData& ic_data, |
| 1032 intptr_t argument_count, | 1227 intptr_t argument_count, |
| 1033 intptr_t deopt_id, | 1228 intptr_t deopt_id, |
| 1034 intptr_t token_pos, | 1229 intptr_t token_pos, |
| 1035 LocationSummary* locs) { | 1230 LocationSummary* locs) { |
| 1036 UNIMPLEMENTED(); | 1231 MegamorphicCacheTable* table = Isolate::Current()->megamorphic_cache_table(); |
| 1232 const String& name = String::Handle(ic_data.target_name()); |
| 1233 const Array& arguments_descriptor = |
| 1234 Array::ZoneHandle(ic_data.arguments_descriptor()); |
| 1235 ASSERT(!arguments_descriptor.IsNull() && (arguments_descriptor.Length() > 0)); |
| 1236 const MegamorphicCache& cache = |
| 1237 MegamorphicCache::ZoneHandle(table->Lookup(name, arguments_descriptor)); |
| 1238 Label not_smi, load_cache; |
| 1239 __ LoadFromOffset(R0, SP, (argument_count - 1) * kWordSize); |
| 1240 __ tsti(R0, kSmiTagMask); |
| 1241 __ b(¬_smi, NE); |
| 1242 __ LoadImmediate(R0, Smi::RawValue(kSmiCid), PP); |
| 1243 __ b(&load_cache); |
| 1244 |
| 1245 __ Bind(¬_smi); |
| 1246 __ LoadClassId(R0, R0); |
| 1247 __ SmiTag(R0); |
| 1248 |
| 1249 // R0: class ID of the receiver (smi). |
| 1250 __ Bind(&load_cache); |
| 1251 __ LoadObject(R1, cache, PP); |
| 1252 __ LoadFieldFromOffset(R2, R1, MegamorphicCache::buckets_offset()); |
| 1253 __ LoadFieldFromOffset(R1, R1, MegamorphicCache::mask_offset()); |
| 1254 // R2: cache buckets array. |
| 1255 // R1: mask. |
| 1256 __ mov(R3, R0); |
| 1257 |
| 1258 Label loop, update, call_target_function; |
| 1259 __ b(&loop); |
| 1260 |
| 1261 __ Bind(&update); |
| 1262 __ add(R3, R3, Operand(Smi::RawValue(1))); |
| 1263 __ Bind(&loop); |
| 1264 __ and_(R3, R3, Operand(R1)); |
| 1265 const intptr_t base = Array::data_offset(); |
| 1266 // R3 is smi tagged, but table entries are 8 bytes, so LSL 2. |
| 1267 __ add(TMP, R2, Operand(R3, LSL, 2)); |
| 1268 __ LoadFieldFromOffset(R4, TMP, base); |
| 1269 |
| 1270 ASSERT(kIllegalCid == 0); |
| 1271 __ tst(R4, Operand(R4)); |
| 1272 __ b(&call_target_function, EQ); |
| 1273 __ CompareRegisters(R4, R0); |
| 1274 __ b(&update, NE); |
| 1275 |
| 1276 __ Bind(&call_target_function); |
| 1277 // Call the target found in the cache. For a class id match, this is a |
| 1278 // proper target for the given name and arguments descriptor. If the |
| 1279 // illegal class id was found, the target is a cache miss handler that can |
| 1280 // be invoked as a normal Dart function. |
| 1281 __ add(TMP, R2, Operand(R3, LSL, 2)); |
| 1282 __ LoadFieldFromOffset(R0, TMP, base + kWordSize); |
| 1283 __ LoadFieldFromOffset(R1, R0, Function::code_offset()); |
| 1284 __ LoadFieldFromOffset(R1, R1, Code::instructions_offset()); |
| 1285 __ LoadObject(R5, ic_data, PP); |
| 1286 __ LoadObject(R4, arguments_descriptor, PP); |
| 1287 __ AddImmediate(R1, R1, Instructions::HeaderSize() - kHeapObjectTag, PP); |
| 1288 __ blr(R1); |
| 1289 AddCurrentDescriptor(PcDescriptors::kOther, Isolate::kNoDeoptId, token_pos); |
| 1290 RecordSafepoint(locs); |
| 1291 AddDeoptIndexAtCall(Isolate::ToDeoptAfter(deopt_id), token_pos); |
| 1292 __ Drop(argument_count); |
| 1037 } | 1293 } |
| 1038 | 1294 |
| 1039 | 1295 |
| 1040 void FlowGraphCompiler::EmitUnoptimizedStaticCall( | 1296 void FlowGraphCompiler::EmitUnoptimizedStaticCall( |
| 1041 const Function& target_function, | 1297 const Function& target_function, |
| 1042 const Array& arguments_descriptor, | 1298 const Array& arguments_descriptor, |
| 1043 intptr_t argument_count, | 1299 intptr_t argument_count, |
| 1044 intptr_t deopt_id, | 1300 intptr_t deopt_id, |
| 1045 intptr_t token_pos, | 1301 intptr_t token_pos, |
| 1046 LocationSummary* locs) { | 1302 LocationSummary* locs) { |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1078 } | 1334 } |
| 1079 | 1335 |
| 1080 | 1336 |
| 1081 void FlowGraphCompiler::EmitOptimizedStaticCall( | 1337 void FlowGraphCompiler::EmitOptimizedStaticCall( |
| 1082 const Function& function, | 1338 const Function& function, |
| 1083 const Array& arguments_descriptor, | 1339 const Array& arguments_descriptor, |
| 1084 intptr_t argument_count, | 1340 intptr_t argument_count, |
| 1085 intptr_t deopt_id, | 1341 intptr_t deopt_id, |
| 1086 intptr_t token_pos, | 1342 intptr_t token_pos, |
| 1087 LocationSummary* locs) { | 1343 LocationSummary* locs) { |
| 1088 UNIMPLEMENTED(); | 1344 __ LoadObject(R4, arguments_descriptor, PP); |
| 1345 // Do not use the code from the function, but let the code be patched so that |
| 1346 // we can record the outgoing edges to other code. |
| 1347 GenerateDartCall(deopt_id, |
| 1348 token_pos, |
| 1349 &StubCode::CallStaticFunctionLabel(), |
| 1350 PcDescriptors::kOptStaticCall, |
| 1351 locs); |
| 1352 AddStaticCallTarget(function); |
| 1353 __ Drop(argument_count); |
| 1089 } | 1354 } |
| 1090 | 1355 |
| 1091 | 1356 |
| 1092 void FlowGraphCompiler::EmitEqualityRegConstCompare(Register reg, | 1357 void FlowGraphCompiler::EmitEqualityRegConstCompare(Register reg, |
| 1093 const Object& obj, | 1358 const Object& obj, |
| 1094 bool needs_number_check, | 1359 bool needs_number_check, |
| 1095 intptr_t token_pos) { | 1360 intptr_t token_pos) { |
| 1096 UNIMPLEMENTED(); | 1361 if (needs_number_check) { |
| 1362 ASSERT(!obj.IsMint() && !obj.IsDouble() && !obj.IsBigint()); |
| 1363 __ Push(reg); |
| 1364 __ PushObject(obj, PP); |
| 1365 if (is_optimizing()) { |
| 1366 __ BranchLinkPatchable( |
| 1367 &StubCode::OptimizedIdenticalWithNumberCheckLabel()); |
| 1368 } else { |
| 1369 __ BranchLinkPatchable( |
| 1370 &StubCode::UnoptimizedIdenticalWithNumberCheckLabel()); |
| 1371 } |
| 1372 if (token_pos != Scanner::kNoSourcePos) { |
| 1373 AddCurrentDescriptor(PcDescriptors::kRuntimeCall, |
| 1374 Isolate::kNoDeoptId, |
| 1375 token_pos); |
| 1376 } |
| 1377 __ Drop(1); // Discard constant. |
| 1378 __ Pop(reg); // Restore 'reg'. |
| 1379 return; |
| 1380 } |
| 1381 |
| 1382 __ CompareObject(reg, obj, PP); |
| 1097 } | 1383 } |
| 1098 | 1384 |
| 1099 | 1385 |
| 1100 void FlowGraphCompiler::EmitEqualityRegRegCompare(Register left, | 1386 void FlowGraphCompiler::EmitEqualityRegRegCompare(Register left, |
| 1101 Register right, | 1387 Register right, |
| 1102 bool needs_number_check, | 1388 bool needs_number_check, |
| 1103 intptr_t token_pos) { | 1389 intptr_t token_pos) { |
| 1104 if (needs_number_check) { | 1390 if (needs_number_check) { |
| 1105 __ Push(left); | 1391 __ Push(left); |
| 1106 __ Push(right); | 1392 __ Push(right); |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1179 | 1465 |
| 1180 | 1466 |
| 1181 void FlowGraphCompiler::EmitTestAndCall(const ICData& ic_data, | 1467 void FlowGraphCompiler::EmitTestAndCall(const ICData& ic_data, |
| 1182 Register class_id_reg, | 1468 Register class_id_reg, |
| 1183 intptr_t argument_count, | 1469 intptr_t argument_count, |
| 1184 const Array& argument_names, | 1470 const Array& argument_names, |
| 1185 Label* deopt, | 1471 Label* deopt, |
| 1186 intptr_t deopt_id, | 1472 intptr_t deopt_id, |
| 1187 intptr_t token_index, | 1473 intptr_t token_index, |
| 1188 LocationSummary* locs) { | 1474 LocationSummary* locs) { |
| 1189 UNIMPLEMENTED(); | 1475 ASSERT(is_optimizing()); |
| 1476 ASSERT(!ic_data.IsNull() && (ic_data.NumberOfChecks() > 0)); |
| 1477 Label match_found; |
| 1478 const intptr_t len = ic_data.NumberOfChecks(); |
| 1479 GrowableArray<CidTarget> sorted(len); |
| 1480 SortICDataByCount(ic_data, &sorted); |
| 1481 ASSERT(class_id_reg != R4); |
| 1482 ASSERT(len > 0); // Why bother otherwise. |
| 1483 const Array& arguments_descriptor = |
| 1484 Array::ZoneHandle(ArgumentsDescriptor::New(argument_count, |
| 1485 argument_names)); |
| 1486 __ LoadObject(R4, arguments_descriptor, PP); |
| 1487 for (intptr_t i = 0; i < len; i++) { |
| 1488 const bool is_last_check = (i == (len - 1)); |
| 1489 Label next_test; |
| 1490 __ CompareImmediate(class_id_reg, sorted[i].cid, PP); |
| 1491 if (is_last_check) { |
| 1492 __ b(deopt, NE); |
| 1493 } else { |
| 1494 __ b(&next_test, NE); |
| 1495 } |
| 1496 // Do not use the code from the function, but let the code be patched so |
| 1497 // that we can record the outgoing edges to other code. |
| 1498 GenerateDartCall(deopt_id, |
| 1499 token_index, |
| 1500 &StubCode::CallStaticFunctionLabel(), |
| 1501 PcDescriptors::kOptStaticCall, |
| 1502 locs); |
| 1503 const Function& function = *sorted[i].target; |
| 1504 AddStaticCallTarget(function); |
| 1505 __ Drop(argument_count); |
| 1506 if (!is_last_check) { |
| 1507 __ b(&match_found); |
| 1508 } |
| 1509 __ Bind(&next_test); |
| 1510 } |
| 1511 __ Bind(&match_found); |
| 1190 } | 1512 } |
| 1191 | 1513 |
| 1192 | 1514 |
| 1193 // Do not implement or use this function. | 1515 // Do not implement or use this function. |
| 1194 FieldAddress FlowGraphCompiler::ElementAddressForIntIndex(intptr_t cid, | 1516 FieldAddress FlowGraphCompiler::ElementAddressForIntIndex(intptr_t cid, |
| 1195 intptr_t index_scale, | 1517 intptr_t index_scale, |
| 1196 Register array, | 1518 Register array, |
| 1197 intptr_t index) { | 1519 intptr_t index) { |
| 1198 UNREACHABLE(); | 1520 UNREACHABLE(); |
| 1199 return FieldAddress(array, index); | 1521 return FieldAddress(array, index); |
| (...skipping 26 matching lines...) Expand all Loading... |
| 1226 UNREACHABLE(); | 1548 UNREACHABLE(); |
| 1227 return FieldAddress(array, index); | 1549 return FieldAddress(array, index); |
| 1228 } | 1550 } |
| 1229 | 1551 |
| 1230 | 1552 |
| 1231 #undef __ | 1553 #undef __ |
| 1232 #define __ compiler_->assembler()-> | 1554 #define __ compiler_->assembler()-> |
| 1233 | 1555 |
| 1234 | 1556 |
| 1235 void ParallelMoveResolver::EmitMove(int index) { | 1557 void ParallelMoveResolver::EmitMove(int index) { |
| 1236 UNIMPLEMENTED(); | 1558 MoveOperands* move = moves_[index]; |
| 1559 const Location source = move->src(); |
| 1560 const Location destination = move->dest(); |
| 1561 |
| 1562 if (source.IsRegister()) { |
| 1563 if (destination.IsRegister()) { |
| 1564 __ mov(destination.reg(), source.reg()); |
| 1565 } else { |
| 1566 ASSERT(destination.IsStackSlot()); |
| 1567 const intptr_t dest_offset = destination.ToStackSlotOffset(); |
| 1568 __ StoreToOffset(source.reg(), FP, dest_offset); |
| 1569 } |
| 1570 } else if (source.IsStackSlot()) { |
| 1571 if (destination.IsRegister()) { |
| 1572 const intptr_t source_offset = source.ToStackSlotOffset(); |
| 1573 __ LoadFromOffset(destination.reg(), FP, source_offset); |
| 1574 } else { |
| 1575 ASSERT(destination.IsStackSlot()); |
| 1576 const intptr_t source_offset = source.ToStackSlotOffset(); |
| 1577 const intptr_t dest_offset = destination.ToStackSlotOffset(); |
| 1578 __ LoadFromOffset(TMP, FP, source_offset); |
| 1579 __ StoreToOffset(TMP, FP, dest_offset); |
| 1580 } |
| 1581 } else if (source.IsFpuRegister()) { |
| 1582 if (destination.IsFpuRegister()) { |
| 1583 __ fmovdd(destination.fpu_reg(), source.fpu_reg()); |
| 1584 } else { |
| 1585 if (destination.IsDoubleStackSlot()) { |
| 1586 const intptr_t dest_offset = destination.ToStackSlotOffset(); |
| 1587 VRegister src = source.fpu_reg(); |
| 1588 __ StoreDToOffset(src, FP, dest_offset); |
| 1589 } else { |
| 1590 ASSERT(destination.IsQuadStackSlot()); |
| 1591 UNIMPLEMENTED(); |
| 1592 } |
| 1593 } |
| 1594 } else if (source.IsDoubleStackSlot()) { |
| 1595 if (destination.IsFpuRegister()) { |
| 1596 const intptr_t dest_offset = source.ToStackSlotOffset(); |
| 1597 const VRegister dst = destination.fpu_reg(); |
| 1598 __ LoadDFromOffset(dst, FP, dest_offset); |
| 1599 } else { |
| 1600 ASSERT(destination.IsDoubleStackSlot()); |
| 1601 const intptr_t source_offset = source.ToStackSlotOffset(); |
| 1602 const intptr_t dest_offset = destination.ToStackSlotOffset(); |
| 1603 __ LoadDFromOffset(VTMP, FP, source_offset); |
| 1604 __ StoreDToOffset(VTMP, FP, dest_offset); |
| 1605 } |
| 1606 } else if (source.IsQuadStackSlot()) { |
| 1607 UNIMPLEMENTED(); |
| 1608 } else { |
| 1609 ASSERT(source.IsConstant()); |
| 1610 const Object& constant = source.constant(); |
| 1611 if (destination.IsRegister()) { |
| 1612 __ LoadObject(destination.reg(), constant, PP); |
| 1613 } else if (destination.IsFpuRegister()) { |
| 1614 const VRegister dst = destination.fpu_reg(); |
| 1615 __ LoadObject(TMP, constant, PP); |
| 1616 __ LoadDFieldFromOffset(dst, TMP, Double::value_offset()); |
| 1617 } else { |
| 1618 ASSERT(destination.IsStackSlot()); |
| 1619 const intptr_t dest_offset = destination.ToStackSlotOffset(); |
| 1620 __ LoadObject(TMP, constant, PP); |
| 1621 __ StoreToOffset(TMP, FP, dest_offset); |
| 1622 } |
| 1623 } |
| 1624 |
| 1625 move->Eliminate(); |
| 1237 } | 1626 } |
| 1238 | 1627 |
| 1239 | 1628 |
| 1240 void ParallelMoveResolver::EmitSwap(int index) { | 1629 void ParallelMoveResolver::EmitSwap(int index) { |
| 1241 UNIMPLEMENTED(); | 1630 MoveOperands* move = moves_[index]; |
| 1631 const Location source = move->src(); |
| 1632 const Location destination = move->dest(); |
| 1633 |
| 1634 if (source.IsRegister() && destination.IsRegister()) { |
| 1635 ASSERT(source.reg() != TMP); |
| 1636 ASSERT(destination.reg() != TMP); |
| 1637 __ mov(TMP, source.reg()); |
| 1638 __ mov(source.reg(), destination.reg()); |
| 1639 __ mov(destination.reg(), TMP); |
| 1640 } else if (source.IsRegister() && destination.IsStackSlot()) { |
| 1641 Exchange(source.reg(), destination.ToStackSlotOffset()); |
| 1642 } else if (source.IsStackSlot() && destination.IsRegister()) { |
| 1643 Exchange(destination.reg(), source.ToStackSlotOffset()); |
| 1644 } else if (source.IsStackSlot() && destination.IsStackSlot()) { |
| 1645 Exchange(source.ToStackSlotOffset(), destination.ToStackSlotOffset()); |
| 1646 } else if (source.IsFpuRegister() && destination.IsFpuRegister()) { |
| 1647 const VRegister dst = destination.fpu_reg(); |
| 1648 const VRegister src = source.fpu_reg(); |
| 1649 __ fmovdd(VTMP, src); |
| 1650 __ fmovdd(src, dst); |
| 1651 __ fmovdd(dst, VTMP); |
| 1652 } else if (source.IsFpuRegister() || destination.IsFpuRegister()) { |
| 1653 ASSERT(destination.IsDoubleStackSlot() || |
| 1654 destination.IsQuadStackSlot() || |
| 1655 source.IsDoubleStackSlot() || |
| 1656 source.IsQuadStackSlot()); |
| 1657 bool double_width = destination.IsDoubleStackSlot() || |
| 1658 source.IsDoubleStackSlot(); |
| 1659 VRegister reg = source.IsFpuRegister() ? source.fpu_reg() |
| 1660 : destination.fpu_reg(); |
| 1661 const intptr_t slot_offset = source.IsFpuRegister() |
| 1662 ? destination.ToStackSlotOffset() |
| 1663 : source.ToStackSlotOffset(); |
| 1664 |
| 1665 if (double_width) { |
| 1666 __ LoadDFromOffset(VTMP, FP, slot_offset); |
| 1667 __ StoreDToOffset(reg, FP, slot_offset); |
| 1668 __ fmovdd(reg, VTMP); |
| 1669 } else { |
| 1670 UNIMPLEMENTED(); |
| 1671 } |
| 1672 } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) { |
| 1673 const intptr_t source_offset = source.ToStackSlotOffset(); |
| 1674 const intptr_t dest_offset = destination.ToStackSlotOffset(); |
| 1675 |
| 1676 ScratchFpuRegisterScope ensure_scratch(this, VTMP); |
| 1677 VRegister scratch = ensure_scratch.reg(); |
| 1678 __ LoadDFromOffset(VTMP, FP, source_offset); |
| 1679 __ LoadDFromOffset(scratch, FP, dest_offset); |
| 1680 __ StoreDToOffset(VTMP, FP, dest_offset); |
| 1681 __ StoreDToOffset(scratch, FP, source_offset); |
| 1682 } else if (source.IsQuadStackSlot() && destination.IsQuadStackSlot()) { |
| 1683 UNIMPLEMENTED(); |
| 1684 } else { |
| 1685 UNREACHABLE(); |
| 1686 } |
| 1687 |
| 1688 // The swap of source and destination has executed a move from source to |
| 1689 // destination. |
| 1690 move->Eliminate(); |
| 1691 |
| 1692 // Any unperformed (including pending) move with a source of either |
| 1693 // this move's source or destination needs to have their source |
| 1694 // changed to reflect the state of affairs after the swap. |
| 1695 for (int i = 0; i < moves_.length(); ++i) { |
| 1696 const MoveOperands& other_move = *moves_[i]; |
| 1697 if (other_move.Blocks(source)) { |
| 1698 moves_[i]->set_src(destination); |
| 1699 } else if (other_move.Blocks(destination)) { |
| 1700 moves_[i]->set_src(source); |
| 1701 } |
| 1702 } |
| 1242 } | 1703 } |
| 1243 | 1704 |
| 1244 | 1705 |
| 1245 void ParallelMoveResolver::MoveMemoryToMemory(const Address& dst, | 1706 void ParallelMoveResolver::MoveMemoryToMemory(const Address& dst, |
| 1246 const Address& src) { | 1707 const Address& src) { |
| 1247 UNIMPLEMENTED(); | 1708 UNIMPLEMENTED(); |
| 1248 } | 1709 } |
| 1249 | 1710 |
| 1250 | 1711 |
| 1251 void ParallelMoveResolver::StoreObject(const Address& dst, const Object& obj) { | 1712 void ParallelMoveResolver::StoreObject(const Address& dst, const Object& obj) { |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1296 void ParallelMoveResolver::RestoreFpuScratch(FpuRegister reg) { | 1757 void ParallelMoveResolver::RestoreFpuScratch(FpuRegister reg) { |
| 1297 UNIMPLEMENTED(); | 1758 UNIMPLEMENTED(); |
| 1298 } | 1759 } |
| 1299 | 1760 |
| 1300 | 1761 |
| 1301 #undef __ | 1762 #undef __ |
| 1302 | 1763 |
| 1303 } // namespace dart | 1764 } // namespace dart |
| 1304 | 1765 |
| 1305 #endif // defined TARGET_ARCH_ARM64 | 1766 #endif // defined TARGET_ARCH_ARM64 |
| OLD | NEW |