| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | |
| 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. | |
| 4 | |
| 5 #include "vm/globals.h" // Needed here to get TARGET_ARCH_IA32. | |
| 6 #if defined(TARGET_ARCH_IA32) | |
| 7 | |
| 8 #include "vm/ic_stubs.h" | |
| 9 | |
| 10 #include "vm/assembler.h" | |
| 11 #include "vm/code_index_table.h" | |
| 12 #include "vm/disassembler.h" | |
| 13 #include "vm/flags.h" | |
| 14 #include "vm/instructions.h" | |
| 15 #include "vm/object.h" | |
| 16 #include "vm/object_store.h" | |
| 17 #include "vm/stub_code.h" | |
| 18 | |
| 19 namespace dart { | |
| 20 | |
| 21 DECLARE_FLAG(bool, disassemble_stubs); | |
| 22 DEFINE_FLAG(bool, enable_polymorphic_ic, true, | |
| 23 "Enable polymorphic inline caching"); | |
| 24 DEFINE_FLAG(bool, trace_icstub_generation, false, | |
| 25 "Print every generated IC stub"); | |
| 26 | |
| 27 | |
| 28 // TODO(srdjan): Move FindInCode and AppendICStubToTargets into the shared file. | |
| 29 | |
| 30 // Add 'classes' and 'ic_stub' to all 'targets'. Each target's code | |
| 31 // (class RawCode) has an array of (classes-array, ic_stub) pairs. | |
| 32 void ICStubs::AppendICStubToTargets( | |
| 33 const GrowableArray<const Function*>& targets, | |
| 34 const GrowableArray<const Class*>& classes, | |
| 35 const Code& ic_stub) { | |
| 36 if (FLAG_trace_icstub_generation) { | |
| 37 OS::Print("Appending ICstub 0x%x to targets:\n", ic_stub.EntryPoint()); | |
| 38 } | |
| 39 Code& target = Code::Handle(); | |
| 40 Array& class_ic_stubs_array = Array::Handle(); | |
| 41 Code& test_ic_stub = Code::Handle(); | |
| 42 for (intptr_t i = 0; i < targets.length(); i++) { | |
| 43 target = Code::Handle(targets[i]->code()).raw(); | |
| 44 if (FLAG_trace_icstub_generation) { | |
| 45 OS::Print(" * code 0x%x\n", target.EntryPoint()); | |
| 46 } | |
| 47 // Do not add twice: two different classes may have the same target. | |
| 48 test_ic_stub = ICStubs::FindInCode(target, classes); | |
| 49 if (test_ic_stub.IsNull()) { | |
| 50 // Append one class/ic-stub pair entry. | |
| 51 // Grow the array by two (one pair). | |
| 52 class_ic_stubs_array = target.class_ic_stubs(); | |
| 53 intptr_t new_length = class_ic_stubs_array.Length() + 2; | |
| 54 class_ic_stubs_array = Array::Grow(class_ic_stubs_array, new_length); | |
| 55 target.set_class_ic_stubs(class_ic_stubs_array); | |
| 56 // Create classes array out of GrowableArray classes. | |
| 57 Array& a = Array::Handle(Array::New(classes.length())); | |
| 58 for (intptr_t i = 0; i < classes.length(); i++) { | |
| 59 a.SetAt(i, *classes[i]); | |
| 60 } | |
| 61 class_ic_stubs_array.SetAt(new_length - 2, a); | |
| 62 class_ic_stubs_array.SetAt(new_length - 1, ic_stub); | |
| 63 if (FLAG_trace_icstub_generation) { | |
| 64 OS::Print(" + icstub 0x%x\n", ic_stub.EntryPoint()); | |
| 65 } | |
| 66 } else { | |
| 67 if (FLAG_trace_icstub_generation) { | |
| 68 OS::Print(" . icstub 0x%x\n", test_ic_stub.EntryPoint()); | |
| 69 } | |
| 70 } | |
| 71 } | |
| 72 } | |
| 73 | |
| 74 | |
| 75 // Return true if class 'test' is contained in array 'classes'. | |
| 76 static bool IsClassInArray(const Class& test, | |
| 77 const GrowableArray<const Class*>& classes) { | |
| 78 for (intptr_t i = 0; i < classes.length(); i++) { | |
| 79 if (classes[i]->raw() == test.raw()) { | |
| 80 return true; | |
| 81 } | |
| 82 } | |
| 83 return false; | |
| 84 } | |
| 85 | |
| 86 | |
| 87 // Linear search for the ic stub with given 'classes'. RawCode::class_ic_stubs() | |
| 88 // returns an array of (classes-array, ic-stub-code) pairs. Returns | |
| 89 // RawCode::null() if no stub is found. | |
| 90 RawCode* ICStubs::FindInCode(const Code& target, | |
| 91 const GrowableArray<const Class*>& classes) { | |
| 92 Code& result = Code::Handle(); | |
| 93 if (classes.is_empty()) { | |
| 94 return result.raw(); // RawCode::null(). | |
| 95 } | |
| 96 Array& class_ic_stubs = Array::Handle(target.class_ic_stubs()); | |
| 97 const intptr_t len = class_ic_stubs.Length(); | |
| 98 Array& array = Array::Handle(); | |
| 99 Class& cls = Class::Handle(); | |
| 100 // Iterate over all stored IC stubs/array classes pairs until match found. | |
| 101 for (intptr_t i = 0; i < len; i += 2) { | |
| 102 // i: array of classes, i + 1: ic stub code. | |
| 103 array ^= class_ic_stubs.At(i); | |
| 104 if (array.Length() == classes.length()) { | |
| 105 bool classes_match = true; | |
| 106 for (intptr_t k = 0; k < array.Length(); k++) { | |
| 107 cls ^= array.At(k); | |
| 108 if (!IsClassInArray(cls, classes)) { | |
| 109 classes_match = false; | |
| 110 break; | |
| 111 } | |
| 112 } | |
| 113 if (classes_match) { | |
| 114 // Found matching stub. | |
| 115 result ^= class_ic_stubs.At(i + 1); | |
| 116 break; | |
| 117 } | |
| 118 } | |
| 119 } | |
| 120 // If no matching stub is found, result.raw() returns null. | |
| 121 return result.raw(); | |
| 122 } | |
| 123 | |
| 124 | |
| 125 int ICStubs::IndexOfClass(const GrowableArray<const Class*>& classes, | |
| 126 const Class& cls) { | |
| 127 for (intptr_t i = 0; i < classes.length(); i++) { | |
| 128 if (classes[i]->raw() == cls.raw()) { | |
| 129 return i; | |
| 130 } | |
| 131 } | |
| 132 return -1; | |
| 133 } | |
| 134 | |
| 135 | |
| 136 // An IC Stub starts with a Smi test, optionally followed by a null test | |
| 137 // and zero or more class tests. The "StubCode::CallInstanceFunction" | |
| 138 // corresponds to an IC stub without any classes or targets. | |
| 139 bool ICStubs::RecognizeICStub(uword ic_entry_point, | |
| 140 GrowableArray<const Class*>* classes, | |
| 141 GrowableArray<const Function*>* targets) { | |
| 142 if (ic_entry_point == StubCode::CallInstanceFunctionLabel().address()) { | |
| 143 // Unresolved instance call, no classes collected yet. | |
| 144 return true; | |
| 145 } | |
| 146 if (ic_entry_point == StubCode::MegamorphicLookupEntryPoint()) { | |
| 147 // NoSuchMethod call, no classes collected. | |
| 148 return true; | |
| 149 } | |
| 150 return ParseICStub(ic_entry_point, classes, targets, 0, 0); | |
| 151 } | |
| 152 | |
| 153 | |
| 154 void ICStubs::PatchTargets(uword ic_entry_point, uword from, uword to) { | |
| 155 bool is_ok = ParseICStub(ic_entry_point, NULL, NULL, from, to); | |
| 156 ASSERT(is_ok); | |
| 157 } | |
| 158 | |
| 159 | |
| 160 // Parse IC stub, collect 'classes' and 'targets' and patches | |
| 161 // all 'from' targets with 'to' targets. No collection occurs | |
| 162 // if 'classes' and 'targets' are NULL, no patching occurs if | |
| 163 // 'from' or 'to' is 0. | |
| 164 // The IC structure is defined in IcStubs::GetIcStub. | |
| 165 bool ICStubs::ParseICStub(uword ic_entry_point, | |
| 166 GrowableArray<const Class*>* classes, | |
| 167 GrowableArray<const Function*>* targets, | |
| 168 uword from, | |
| 169 uword to) { | |
| 170 uword instruction_address = ic_entry_point; | |
| 171 bool patch_code = (from != 0) && (to != 0); | |
| 172 if (classes != NULL) { | |
| 173 classes->Clear(); | |
| 174 } | |
| 175 if (targets != NULL) { | |
| 176 targets->Clear(); | |
| 177 } | |
| 178 | |
| 179 // Part A: Load receiver, test if Smi, jump to IC miss or hit. | |
| 180 ICLoadReceiver load_receiver(instruction_address); | |
| 181 if (!load_receiver.IsValid()) { | |
| 182 return false; // Not an an IC stub. | |
| 183 } | |
| 184 instruction_address += load_receiver.pattern_length_in_bytes(); | |
| 185 | |
| 186 TestEaxIsSmi test_smi(instruction_address); | |
| 187 // The target of the Smi test determines if the test should cause | |
| 188 // IC miss if successful or jump to target (IC hit). Target of IC miss is the | |
| 189 // stub code, target of IC success is a code object. | |
| 190 CodeIndexTable* ci_table = Isolate::Current()->code_index_table(); | |
| 191 ASSERT(ci_table != NULL); | |
| 192 | |
| 193 Instructions& inst = Instructions::Handle( | |
| 194 Instructions::FromEntryPoint(ic_entry_point)); | |
| 195 ASSERT(!inst.IsNull()); | |
| 196 | |
| 197 if (!StubCode::InCallInstanceFunctionStubCode(test_smi.TargetAddress())) { | |
| 198 // Jump is an IC success. | |
| 199 if (patch_code && (test_smi.TargetAddress() == from)) { | |
| 200 test_smi.SetTargetAddress(to); | |
| 201 } | |
| 202 const Class& smi_class = | |
| 203 Class::ZoneHandle(Isolate::Current()->object_store()->smi_class()); | |
| 204 const Code& smi_code = | |
| 205 Code::Handle(ci_table->LookupCode(test_smi.TargetAddress())); | |
| 206 ASSERT(!smi_class.IsNullClass()); | |
| 207 ASSERT(!smi_code.IsNull()); | |
| 208 if (classes != NULL) { | |
| 209 classes->Add(&smi_class); | |
| 210 } | |
| 211 if (targets != NULL) { | |
| 212 targets->Add(&Function::ZoneHandle(smi_code.function())); | |
| 213 } | |
| 214 } | |
| 215 instruction_address += test_smi.pattern_length_in_bytes(); | |
| 216 | |
| 217 // TODO(srdjan): Add checks that the IC stub ends with | |
| 218 // a null check and a jmp. | |
| 219 // Part B: Load receiver's class, compare with all known classes. | |
| 220 LoadObjectClass load_object_class(instruction_address); | |
| 221 if (!load_object_class.IsValid()) { | |
| 222 return false; | |
| 223 } | |
| 224 instruction_address += load_object_class.pattern_length_in_bytes(); | |
| 225 while (true) { | |
| 226 ICCheckReceiverClass check_class(instruction_address); | |
| 227 if (!check_class.IsValid()) { | |
| 228 // Done parsing. | |
| 229 return true; | |
| 230 } | |
| 231 if (patch_code && (check_class.TargetAddress() == from)) { | |
| 232 check_class.SetTargetAddress(to); | |
| 233 } | |
| 234 const Class& cls = Class::ZoneHandle(check_class.TestClass()); | |
| 235 const Code& code = Code::ZoneHandle( | |
| 236 ci_table->LookupCode(check_class.TargetAddress())); | |
| 237 ASSERT(!cls.IsNullClass()); | |
| 238 ASSERT(!code.IsNull()); | |
| 239 if (classes != NULL) { | |
| 240 classes->Add(&cls); | |
| 241 } | |
| 242 if (targets != NULL) { | |
| 243 targets->Add(&Function::ZoneHandle(code.function())); | |
| 244 } | |
| 245 instruction_address += check_class.pattern_length_in_bytes(); | |
| 246 } | |
| 247 } | |
| 248 | |
| 249 | |
| 250 // Generate inline cache stub for given targets and classes. | |
| 251 // EDX: arguments descriptor array (preserved). | |
| 252 // ECX: function name (unused, preserved). | |
| 253 // TOS: return address. | |
| 254 // Jump to target if the receiver's class matches the 'receiver_class'. | |
| 255 // Otherwise jump to megamorphic lookup. TODO(srdjan): Patch call site to go to | |
| 256 // megamorphic instead of going via the IC stub. | |
| 257 // IC stub structure: | |
| 258 // A: Get receiver, test if Smi, jump to IC miss or hit. | |
| 259 // B: Get receiver's class, compare with all known classes. | |
| 260 RawCode* ICStubs::GetICStub(const GrowableArray<const Class*>& classes, | |
| 261 const GrowableArray<const Function*>& targets) { | |
| 262 // Check if a matching IC stub already exists. | |
| 263 Code& ic_stub_code = Code::Handle(); | |
| 264 for (intptr_t i = 0; i < targets.length(); i++) { | |
| 265 ic_stub_code = | |
| 266 ICStubs::FindInCode(Code::Handle(targets[i]->code()), classes); | |
| 267 if (!ic_stub_code.IsNull()) { | |
| 268 return ic_stub_code.raw(); // Reusing the previously created IC stub. | |
| 269 } | |
| 270 } | |
| 271 | |
| 272 // Call IC miss handling only if polymorphic inline caching is on, otherwise | |
| 273 // continue in megamorphic lookup. | |
| 274 const ExternalLabel* ic_miss_label = | |
| 275 FLAG_enable_polymorphic_ic | |
| 276 ? &StubCode::CallInstanceFunctionLabel() | |
| 277 : &StubCode::MegamorphicLookupLabel(); | |
| 278 | |
| 279 #define __ assembler. | |
| 280 Assembler assembler; | |
| 281 // Part A: Get receiver, test if Smi. | |
| 282 // Total number of args is the first Smi in args descriptor array (EDX). | |
| 283 __ movl(EAX, FieldAddress(EDX, Array::data_offset())); | |
| 284 __ movl(EAX, Address(ESP, EAX, TIMES_2, 0)); // Get receiver. EAX is a Smi. | |
| 285 __ testl(EAX, Immediate(kSmiTagMask)); | |
| 286 | |
| 287 const Class& smi_class = Class::Handle( | |
| 288 Isolate::Current()->object_store()->smi_class()); | |
| 289 const int smi_class_index = IndexOfClass(classes, smi_class); | |
| 290 if (smi_class_index >= 0) { | |
| 291 // Smi is not IC miss. | |
| 292 const Code& target = Code::Handle(targets[smi_class_index]->code()); | |
| 293 ExternalLabel target_label("ICtoTargetSmi", target.EntryPoint()); | |
| 294 // Always check for Smi first and either go to target or call ic-miss. | |
| 295 __ j(ZERO, &target_label); | |
| 296 } else { | |
| 297 // Smi is IC miss. | |
| 298 __ j(ZERO, ic_miss_label); | |
| 299 } | |
| 300 | |
| 301 // Part B: Load receiver's class, compare with all known classes. | |
| 302 __ movl(EBX, FieldAddress(EAX, Object::class_offset())); | |
| 303 for (int cli = 0; cli < classes.length(); cli++) { | |
| 304 const Class* test_class = classes[cli]; | |
| 305 ASSERT(!test_class->IsNullClass()); | |
| 306 if (test_class->raw() != smi_class.raw()) { | |
| 307 const Code& target = Code::Handle(targets[cli]->code()); | |
| 308 ExternalLabel target_label("ICtoTargetClass", target.EntryPoint()); | |
| 309 __ CompareObject(EBX, *test_class); | |
| 310 __ j(EQUAL, &target_label); | |
| 311 } | |
| 312 } | |
| 313 // IC miss. If null jump to megamorphic (don't trash IC), otherwise to IC | |
| 314 // miss in order to update the IC. | |
| 315 const Immediate raw_null = | |
| 316 Immediate(reinterpret_cast<intptr_t>(Object::null())); | |
| 317 __ cmpl(EAX, raw_null); | |
| 318 __ j(EQUAL, &StubCode::MegamorphicLookupLabel()); | |
| 319 | |
| 320 __ jmp(ic_miss_label); | |
| 321 ic_stub_code = Code::FinalizeCode("inline cache stub", &assembler); | |
| 322 ICStubs::AppendICStubToTargets(targets, classes, ic_stub_code); | |
| 323 | |
| 324 if (FLAG_trace_icstub_generation) { | |
| 325 OS::Print("IC Stub code generated at 0x%x: targets: %d, classes: %d\n", | |
| 326 ic_stub_code.EntryPoint(), targets.length(), classes.length()); | |
| 327 for (intptr_t i = 0; i < targets.length(); i++) { | |
| 328 OS::Print(" target: 0x%x class: %s\n", | |
| 329 Code::Handle(targets[i]->code()).EntryPoint(), | |
| 330 classes[i]->ToCString()); | |
| 331 } | |
| 332 } | |
| 333 | |
| 334 if (FLAG_disassemble_stubs) { | |
| 335 for (intptr_t i = 0; i < classes.length(); i++) { | |
| 336 ASSERT(classes[i]->raw() != Object::null_class()); | |
| 337 const String& class_name = String::Handle(classes[i]->Name()); | |
| 338 CodeIndexTable* code_index_table = Isolate::Current()->code_index_table(); | |
| 339 const Code& target = Code::Handle(targets[i]->code()); | |
| 340 const Function& function = | |
| 341 Function::Handle( | |
| 342 code_index_table->LookupFunction(target.EntryPoint())); | |
| 343 OS::Print("%d: Code for inline cache for class '%s' function '%s': {\n", | |
| 344 i, class_name.ToCString(), function.ToFullyQualifiedCString()); | |
| 345 } | |
| 346 Disassembler::Disassemble(ic_stub_code.EntryPoint(), | |
| 347 ic_stub_code.EntryPoint() + assembler.CodeSize()); | |
| 348 OS::Print("}\n"); | |
| 349 } | |
| 350 | |
| 351 return ic_stub_code.raw(); | |
| 352 #undef __ | |
| 353 } | |
| 354 | |
| 355 | |
| 356 } // namespace dart | |
| 357 | |
| 358 #endif // defined TARGET_ARCH_IA32 | |
| OLD | NEW |