Index: vm/ic_stubs_ia32.cc |
=================================================================== |
--- vm/ic_stubs_ia32.cc (revision 700) |
+++ vm/ic_stubs_ia32.cc (working copy) |
@@ -1,358 +0,0 @@ |
-// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
-// for details. All rights reserved. Use of this source code is governed by a |
-// BSD-style license that can be found in the LICENSE file. |
- |
-#include "vm/globals.h" // Needed here to get TARGET_ARCH_IA32. |
-#if defined(TARGET_ARCH_IA32) |
- |
-#include "vm/ic_stubs.h" |
- |
-#include "vm/assembler.h" |
-#include "vm/code_index_table.h" |
-#include "vm/disassembler.h" |
-#include "vm/flags.h" |
-#include "vm/instructions.h" |
-#include "vm/object.h" |
-#include "vm/object_store.h" |
-#include "vm/stub_code.h" |
- |
-namespace dart { |
- |
-DECLARE_FLAG(bool, disassemble_stubs); |
-DEFINE_FLAG(bool, enable_polymorphic_ic, true, |
- "Enable polymorphic inline caching"); |
-DEFINE_FLAG(bool, trace_icstub_generation, false, |
- "Print every generated IC stub"); |
- |
- |
-// TODO(srdjan): Move FindInCode and AppendICStubToTargets into the shared file. |
- |
-// Add 'classes' and 'ic_stub' to all 'targets'. Each target's code |
-// (class RawCode) has an array of (classes-array, ic_stub) pairs. |
-void ICStubs::AppendICStubToTargets( |
- const GrowableArray<const Function*>& targets, |
- const GrowableArray<const Class*>& classes, |
- const Code& ic_stub) { |
- if (FLAG_trace_icstub_generation) { |
- OS::Print("Appending ICstub 0x%x to targets:\n", ic_stub.EntryPoint()); |
- } |
- Code& target = Code::Handle(); |
- Array& class_ic_stubs_array = Array::Handle(); |
- Code& test_ic_stub = Code::Handle(); |
- for (intptr_t i = 0; i < targets.length(); i++) { |
- target = Code::Handle(targets[i]->code()).raw(); |
- if (FLAG_trace_icstub_generation) { |
- OS::Print(" * code 0x%x\n", target.EntryPoint()); |
- } |
- // Do not add twice: two different classes may have the same target. |
- test_ic_stub = ICStubs::FindInCode(target, classes); |
- if (test_ic_stub.IsNull()) { |
- // Append one class/ic-stub pair entry. |
- // Grow the array by two (one pair). |
- class_ic_stubs_array = target.class_ic_stubs(); |
- intptr_t new_length = class_ic_stubs_array.Length() + 2; |
- class_ic_stubs_array = Array::Grow(class_ic_stubs_array, new_length); |
- target.set_class_ic_stubs(class_ic_stubs_array); |
- // Create classes array out of GrowableArray classes. |
- Array& a = Array::Handle(Array::New(classes.length())); |
- for (intptr_t i = 0; i < classes.length(); i++) { |
- a.SetAt(i, *classes[i]); |
- } |
- class_ic_stubs_array.SetAt(new_length - 2, a); |
- class_ic_stubs_array.SetAt(new_length - 1, ic_stub); |
- if (FLAG_trace_icstub_generation) { |
- OS::Print(" + icstub 0x%x\n", ic_stub.EntryPoint()); |
- } |
- } else { |
- if (FLAG_trace_icstub_generation) { |
- OS::Print(" . icstub 0x%x\n", test_ic_stub.EntryPoint()); |
- } |
- } |
- } |
-} |
- |
- |
-// Return true if class 'test' is contained in array 'classes'. |
-static bool IsClassInArray(const Class& test, |
- const GrowableArray<const Class*>& classes) { |
- for (intptr_t i = 0; i < classes.length(); i++) { |
- if (classes[i]->raw() == test.raw()) { |
- return true; |
- } |
- } |
- return false; |
-} |
- |
- |
-// Linear search for the ic stub with given 'classes'. RawCode::class_ic_stubs() |
-// returns an array of (classes-array, ic-stub-code) pairs. Returns |
-// RawCode::null() if no stub is found. |
-RawCode* ICStubs::FindInCode(const Code& target, |
- const GrowableArray<const Class*>& classes) { |
- Code& result = Code::Handle(); |
- if (classes.is_empty()) { |
- return result.raw(); // RawCode::null(). |
- } |
- Array& class_ic_stubs = Array::Handle(target.class_ic_stubs()); |
- const intptr_t len = class_ic_stubs.Length(); |
- Array& array = Array::Handle(); |
- Class& cls = Class::Handle(); |
- // Iterate over all stored IC stubs/array classes pairs until match found. |
- for (intptr_t i = 0; i < len; i += 2) { |
- // i: array of classes, i + 1: ic stub code. |
- array ^= class_ic_stubs.At(i); |
- if (array.Length() == classes.length()) { |
- bool classes_match = true; |
- for (intptr_t k = 0; k < array.Length(); k++) { |
- cls ^= array.At(k); |
- if (!IsClassInArray(cls, classes)) { |
- classes_match = false; |
- break; |
- } |
- } |
- if (classes_match) { |
- // Found matching stub. |
- result ^= class_ic_stubs.At(i + 1); |
- break; |
- } |
- } |
- } |
- // If no matching stub is found, result.raw() returns null. |
- return result.raw(); |
-} |
- |
- |
-int ICStubs::IndexOfClass(const GrowableArray<const Class*>& classes, |
- const Class& cls) { |
- for (intptr_t i = 0; i < classes.length(); i++) { |
- if (classes[i]->raw() == cls.raw()) { |
- return i; |
- } |
- } |
- return -1; |
-} |
- |
- |
-// An IC Stub starts with a Smi test, optionally followed by a null test |
-// and zero or more class tests. The "StubCode::CallInstanceFunction" |
-// corresponds to an IC stub without any classes or targets. |
-bool ICStubs::RecognizeICStub(uword ic_entry_point, |
- GrowableArray<const Class*>* classes, |
- GrowableArray<const Function*>* targets) { |
- if (ic_entry_point == StubCode::CallInstanceFunctionLabel().address()) { |
- // Unresolved instance call, no classes collected yet. |
- return true; |
- } |
- if (ic_entry_point == StubCode::MegamorphicLookupEntryPoint()) { |
- // NoSuchMethod call, no classes collected. |
- return true; |
- } |
- return ParseICStub(ic_entry_point, classes, targets, 0, 0); |
-} |
- |
- |
-void ICStubs::PatchTargets(uword ic_entry_point, uword from, uword to) { |
- bool is_ok = ParseICStub(ic_entry_point, NULL, NULL, from, to); |
- ASSERT(is_ok); |
-} |
- |
- |
-// Parse IC stub, collect 'classes' and 'targets' and patches |
-// all 'from' targets with 'to' targets. No collection occurs |
-// if 'classes' and 'targets' are NULL, no patching occurs if |
-// 'from' or 'to' is 0. |
-// The IC structure is defined in IcStubs::GetIcStub. |
-bool ICStubs::ParseICStub(uword ic_entry_point, |
- GrowableArray<const Class*>* classes, |
- GrowableArray<const Function*>* targets, |
- uword from, |
- uword to) { |
- uword instruction_address = ic_entry_point; |
- bool patch_code = (from != 0) && (to != 0); |
- if (classes != NULL) { |
- classes->Clear(); |
- } |
- if (targets != NULL) { |
- targets->Clear(); |
- } |
- |
- // Part A: Load receiver, test if Smi, jump to IC miss or hit. |
- ICLoadReceiver load_receiver(instruction_address); |
- if (!load_receiver.IsValid()) { |
- return false; // Not an an IC stub. |
- } |
- instruction_address += load_receiver.pattern_length_in_bytes(); |
- |
- TestEaxIsSmi test_smi(instruction_address); |
- // The target of the Smi test determines if the test should cause |
- // IC miss if successful or jump to target (IC hit). Target of IC miss is the |
- // stub code, target of IC success is a code object. |
- CodeIndexTable* ci_table = Isolate::Current()->code_index_table(); |
- ASSERT(ci_table != NULL); |
- |
- Instructions& inst = Instructions::Handle( |
- Instructions::FromEntryPoint(ic_entry_point)); |
- ASSERT(!inst.IsNull()); |
- |
- if (!StubCode::InCallInstanceFunctionStubCode(test_smi.TargetAddress())) { |
- // Jump is an IC success. |
- if (patch_code && (test_smi.TargetAddress() == from)) { |
- test_smi.SetTargetAddress(to); |
- } |
- const Class& smi_class = |
- Class::ZoneHandle(Isolate::Current()->object_store()->smi_class()); |
- const Code& smi_code = |
- Code::Handle(ci_table->LookupCode(test_smi.TargetAddress())); |
- ASSERT(!smi_class.IsNullClass()); |
- ASSERT(!smi_code.IsNull()); |
- if (classes != NULL) { |
- classes->Add(&smi_class); |
- } |
- if (targets != NULL) { |
- targets->Add(&Function::ZoneHandle(smi_code.function())); |
- } |
- } |
- instruction_address += test_smi.pattern_length_in_bytes(); |
- |
- // TODO(srdjan): Add checks that the IC stub ends with |
- // a null check and a jmp. |
- // Part B: Load receiver's class, compare with all known classes. |
- LoadObjectClass load_object_class(instruction_address); |
- if (!load_object_class.IsValid()) { |
- return false; |
- } |
- instruction_address += load_object_class.pattern_length_in_bytes(); |
- while (true) { |
- ICCheckReceiverClass check_class(instruction_address); |
- if (!check_class.IsValid()) { |
- // Done parsing. |
- return true; |
- } |
- if (patch_code && (check_class.TargetAddress() == from)) { |
- check_class.SetTargetAddress(to); |
- } |
- const Class& cls = Class::ZoneHandle(check_class.TestClass()); |
- const Code& code = Code::ZoneHandle( |
- ci_table->LookupCode(check_class.TargetAddress())); |
- ASSERT(!cls.IsNullClass()); |
- ASSERT(!code.IsNull()); |
- if (classes != NULL) { |
- classes->Add(&cls); |
- } |
- if (targets != NULL) { |
- targets->Add(&Function::ZoneHandle(code.function())); |
- } |
- instruction_address += check_class.pattern_length_in_bytes(); |
- } |
-} |
- |
- |
-// Generate inline cache stub for given targets and classes. |
-// EDX: arguments descriptor array (preserved). |
-// ECX: function name (unused, preserved). |
-// TOS: return address. |
-// Jump to target if the receiver's class matches the 'receiver_class'. |
-// Otherwise jump to megamorphic lookup. TODO(srdjan): Patch call site to go to |
-// megamorphic instead of going via the IC stub. |
-// IC stub structure: |
-// A: Get receiver, test if Smi, jump to IC miss or hit. |
-// B: Get receiver's class, compare with all known classes. |
-RawCode* ICStubs::GetICStub(const GrowableArray<const Class*>& classes, |
- const GrowableArray<const Function*>& targets) { |
- // Check if a matching IC stub already exists. |
- Code& ic_stub_code = Code::Handle(); |
- for (intptr_t i = 0; i < targets.length(); i++) { |
- ic_stub_code = |
- ICStubs::FindInCode(Code::Handle(targets[i]->code()), classes); |
- if (!ic_stub_code.IsNull()) { |
- return ic_stub_code.raw(); // Reusing the previously created IC stub. |
- } |
- } |
- |
- // Call IC miss handling only if polymorphic inline caching is on, otherwise |
- // continue in megamorphic lookup. |
- const ExternalLabel* ic_miss_label = |
- FLAG_enable_polymorphic_ic |
- ? &StubCode::CallInstanceFunctionLabel() |
- : &StubCode::MegamorphicLookupLabel(); |
- |
-#define __ assembler. |
- Assembler assembler; |
- // Part A: Get receiver, test if Smi. |
- // Total number of args is the first Smi in args descriptor array (EDX). |
- __ movl(EAX, FieldAddress(EDX, Array::data_offset())); |
- __ movl(EAX, Address(ESP, EAX, TIMES_2, 0)); // Get receiver. EAX is a Smi. |
- __ testl(EAX, Immediate(kSmiTagMask)); |
- |
- const Class& smi_class = Class::Handle( |
- Isolate::Current()->object_store()->smi_class()); |
- const int smi_class_index = IndexOfClass(classes, smi_class); |
- if (smi_class_index >= 0) { |
- // Smi is not IC miss. |
- const Code& target = Code::Handle(targets[smi_class_index]->code()); |
- ExternalLabel target_label("ICtoTargetSmi", target.EntryPoint()); |
- // Always check for Smi first and either go to target or call ic-miss. |
- __ j(ZERO, &target_label); |
- } else { |
- // Smi is IC miss. |
- __ j(ZERO, ic_miss_label); |
- } |
- |
- // Part B: Load receiver's class, compare with all known classes. |
- __ movl(EBX, FieldAddress(EAX, Object::class_offset())); |
- for (int cli = 0; cli < classes.length(); cli++) { |
- const Class* test_class = classes[cli]; |
- ASSERT(!test_class->IsNullClass()); |
- if (test_class->raw() != smi_class.raw()) { |
- const Code& target = Code::Handle(targets[cli]->code()); |
- ExternalLabel target_label("ICtoTargetClass", target.EntryPoint()); |
- __ CompareObject(EBX, *test_class); |
- __ j(EQUAL, &target_label); |
- } |
- } |
- // IC miss. If null jump to megamorphic (don't trash IC), otherwise to IC |
- // miss in order to update the IC. |
- const Immediate raw_null = |
- Immediate(reinterpret_cast<intptr_t>(Object::null())); |
- __ cmpl(EAX, raw_null); |
- __ j(EQUAL, &StubCode::MegamorphicLookupLabel()); |
- |
- __ jmp(ic_miss_label); |
- ic_stub_code = Code::FinalizeCode("inline cache stub", &assembler); |
- ICStubs::AppendICStubToTargets(targets, classes, ic_stub_code); |
- |
- if (FLAG_trace_icstub_generation) { |
- OS::Print("IC Stub code generated at 0x%x: targets: %d, classes: %d\n", |
- ic_stub_code.EntryPoint(), targets.length(), classes.length()); |
- for (intptr_t i = 0; i < targets.length(); i++) { |
- OS::Print(" target: 0x%x class: %s\n", |
- Code::Handle(targets[i]->code()).EntryPoint(), |
- classes[i]->ToCString()); |
- } |
- } |
- |
- if (FLAG_disassemble_stubs) { |
- for (intptr_t i = 0; i < classes.length(); i++) { |
- ASSERT(classes[i]->raw() != Object::null_class()); |
- const String& class_name = String::Handle(classes[i]->Name()); |
- CodeIndexTable* code_index_table = Isolate::Current()->code_index_table(); |
- const Code& target = Code::Handle(targets[i]->code()); |
- const Function& function = |
- Function::Handle( |
- code_index_table->LookupFunction(target.EntryPoint())); |
- OS::Print("%d: Code for inline cache for class '%s' function '%s': {\n", |
- i, class_name.ToCString(), function.ToFullyQualifiedCString()); |
- } |
- Disassembler::Disassemble(ic_stub_code.EntryPoint(), |
- ic_stub_code.EntryPoint() + assembler.CodeSize()); |
- OS::Print("}\n"); |
- } |
- |
- return ic_stub_code.raw(); |
-#undef __ |
-} |
- |
- |
-} // namespace dart |
- |
-#endif // defined TARGET_ARCH_IA32 |