| 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
 | 
| 
 |