| Index: src/ia32/code-stubs-ia32.cc
 | 
| ===================================================================
 | 
| --- src/ia32/code-stubs-ia32.cc	(revision 6167)
 | 
| +++ src/ia32/code-stubs-ia32.cc	(working copy)
 | 
| @@ -4973,7 +4973,26 @@
 | 
|  }
 | 
|  
 | 
|  
 | 
| +// Generate stub code for instanceof.
 | 
| +// This code can patch a call site inlined cache of the instance of check,
 | 
| +// which looks like this.
 | 
| +//
 | 
| +//   81 ff XX XX XX XX   cmp    edi, <the hole, patched to a map>
 | 
| +//   75 0a               jne    <some near label>
 | 
| +//   b8 XX XX XX XX      mov    eax, <the hole, patched to either true or false>
 | 
| +//
 | 
| +// If call site patching is requested the stack will have the delta from the
 | 
| +// return address to the cmp instruction just below the return address. This
 | 
| +// also means that call site patching can only take place with arguments in
 | 
| +// registers. TOS looks like this when call site patching is requested
 | 
| +//
 | 
| +//   esp[0] : return address
 | 
| +//   esp[4] : delta from return address to cmp instruction
 | 
| +//
 | 
|  void InstanceofStub::Generate(MacroAssembler* masm) {
 | 
| +  // Call site inlining and patching implies arguments in registers.
 | 
| +  ASSERT(HasArgsInRegisters() || !HasCallSiteInlineCheck());
 | 
| +
 | 
|    // Fixed register usage throughout the stub.
 | 
|    Register object = eax;  // Object (lhs).
 | 
|    Register map = ebx;  // Map of the object.
 | 
| @@ -4981,9 +5000,22 @@
 | 
|    Register prototype = edi;  // Prototype of the function.
 | 
|    Register scratch = ecx;
 | 
|  
 | 
| +  // Constants describing the call site code to patch.
 | 
| +  static const int kDeltaToCmpImmediate = 2;
 | 
| +  static const int kDeltaToMov = 8;
 | 
| +  static const int kDeltaToMovImmediate = 9;
 | 
| +  static const int8_t kCmpEdiImmediateByte1 = static_cast<int8_t>(0x81);
 | 
| +  static const int8_t kCmpEdiImmediateByte2 = static_cast<int8_t>(0xff);
 | 
| +  static const int8_t kMovEaxImmediateByte = static_cast<int8_t>(0xb8);
 | 
| +
 | 
| +  ExternalReference roots_address = ExternalReference::roots_address();
 | 
| +
 | 
| +  ASSERT_EQ(object.code(), InstanceofStub::left().code());
 | 
| +  ASSERT_EQ(function.code(), InstanceofStub::right().code());
 | 
| +
 | 
|    // Get the object and function - they are always both needed.
 | 
|    Label slow, not_js_object;
 | 
| -  if (!args_in_registers()) {
 | 
| +  if (!HasArgsInRegisters()) {
 | 
|      __ mov(object, Operand(esp, 2 * kPointerSize));
 | 
|      __ mov(function, Operand(esp, 1 * kPointerSize));
 | 
|    }
 | 
| @@ -4993,22 +5025,26 @@
 | 
|    __ j(zero, ¬_js_object, not_taken);
 | 
|    __ IsObjectJSObjectType(object, map, scratch, ¬_js_object);
 | 
|  
 | 
| -  // Look up the function and the map in the instanceof cache.
 | 
| -  NearLabel miss;
 | 
| -  ExternalReference roots_address = ExternalReference::roots_address();
 | 
| -  __ mov(scratch, Immediate(Heap::kInstanceofCacheFunctionRootIndex));
 | 
| -  __ cmp(function,
 | 
| -         Operand::StaticArray(scratch, times_pointer_size, roots_address));
 | 
| -  __ j(not_equal, &miss);
 | 
| -  __ mov(scratch, Immediate(Heap::kInstanceofCacheMapRootIndex));
 | 
| -  __ cmp(map, Operand::StaticArray(scratch, times_pointer_size, roots_address));
 | 
| -  __ j(not_equal, &miss);
 | 
| -  __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex));
 | 
| -  __ mov(eax, Operand::StaticArray(scratch, times_pointer_size, roots_address));
 | 
| -  __ IncrementCounter(&Counters::instance_of_cache, 1);
 | 
| -  __ ret((args_in_registers() ? 0 : 2) * kPointerSize);
 | 
| +  // If there is a call site cache don't look in the global cache, but do the
 | 
| +  // real lookup and update the call site cache.
 | 
| +  if (!HasCallSiteInlineCheck()) {
 | 
| +    // Look up the function and the map in the instanceof cache.
 | 
| +    NearLabel miss;
 | 
| +    __ mov(scratch, Immediate(Heap::kInstanceofCacheFunctionRootIndex));
 | 
| +    __ cmp(function,
 | 
| +           Operand::StaticArray(scratch, times_pointer_size, roots_address));
 | 
| +    __ j(not_equal, &miss);
 | 
| +    __ mov(scratch, Immediate(Heap::kInstanceofCacheMapRootIndex));
 | 
| +    __ cmp(map, Operand::StaticArray(
 | 
| +        scratch, times_pointer_size, roots_address));
 | 
| +    __ j(not_equal, &miss);
 | 
| +    __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex));
 | 
| +    __ mov(eax, Operand::StaticArray(
 | 
| +        scratch, times_pointer_size, roots_address));
 | 
| +    __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
 | 
| +    __ bind(&miss);
 | 
| +  }
 | 
|  
 | 
| -  __ bind(&miss);
 | 
|    // Get the prototype of the function.
 | 
|    __ TryGetFunctionPrototype(function, prototype, scratch, &slow);
 | 
|  
 | 
| @@ -5017,13 +5053,29 @@
 | 
|    __ j(zero, &slow, not_taken);
 | 
|    __ IsObjectJSObjectType(prototype, scratch, scratch, &slow);
 | 
|  
 | 
| -  // Update the golbal instanceof cache with the current map and function. The
 | 
| -  // cached answer will be set when it is known.
 | 
| +  // Update the global instanceof or call site inlined cache with the current
 | 
| +  // map and function. The cached answer will be set when it is known below.
 | 
| +  if (!HasCallSiteInlineCheck()) {
 | 
|    __ mov(scratch, Immediate(Heap::kInstanceofCacheMapRootIndex));
 | 
|    __ mov(Operand::StaticArray(scratch, times_pointer_size, roots_address), map);
 | 
|    __ mov(scratch, Immediate(Heap::kInstanceofCacheFunctionRootIndex));
 | 
|    __ mov(Operand::StaticArray(scratch, times_pointer_size, roots_address),
 | 
|           function);
 | 
| +  } else {
 | 
| +    // The constants for the code patching are based on no push instructions
 | 
| +    // at the call site.
 | 
| +    ASSERT(HasArgsInRegisters());
 | 
| +    // Get return address and delta to inlined map check.
 | 
| +    __ mov(scratch, Operand(esp, 0 * kPointerSize));
 | 
| +    __ sub(scratch, Operand(esp, 1 * kPointerSize));
 | 
| +    if (FLAG_debug_code) {
 | 
| +      __ cmpb(Operand(scratch, 0), kCmpEdiImmediateByte1);
 | 
| +      __ Assert(equal, "InstanceofStub unexpected call site cache (cmp 1)");
 | 
| +      __ cmpb(Operand(scratch, 1), kCmpEdiImmediateByte2);
 | 
| +      __ Assert(equal, "InstanceofStub unexpected call site cache (cmp 2)");
 | 
| +    }
 | 
| +    __ mov(Operand(scratch, kDeltaToCmpImmediate), map);
 | 
| +  }
 | 
|  
 | 
|    // Loop through the prototype chain of the object looking for the function
 | 
|    // prototype.
 | 
| @@ -5039,18 +5091,48 @@
 | 
|    __ jmp(&loop);
 | 
|  
 | 
|    __ bind(&is_instance);
 | 
| -  __ IncrementCounter(&Counters::instance_of_stub_true, 1);
 | 
| -  __ Set(eax, Immediate(0));
 | 
| -  __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex));
 | 
| -  __ mov(Operand::StaticArray(scratch, times_pointer_size, roots_address), eax);
 | 
| -  __ ret((args_in_registers() ? 0 : 2) * kPointerSize);
 | 
| +  if (!HasCallSiteInlineCheck()) {
 | 
| +    __ Set(eax, Immediate(0));
 | 
| +    __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex));
 | 
| +    __ mov(Operand::StaticArray(scratch,
 | 
| +                                times_pointer_size, roots_address), eax);
 | 
| +  } else {
 | 
| +    // Get return address and delta to inlined map check.
 | 
| +    __ mov(eax, Factory::true_value());
 | 
| +    __ mov(scratch, Operand(esp, 0 * kPointerSize));
 | 
| +    __ sub(scratch, Operand(esp, 1 * kPointerSize));
 | 
| +    if (FLAG_debug_code) {
 | 
| +      __ cmpb(Operand(scratch, kDeltaToMov), kMovEaxImmediateByte);
 | 
| +      __ Assert(equal, "InstanceofStub unexpected call site cache (mov)");
 | 
| +    }
 | 
| +    __ mov(Operand(scratch, kDeltaToMovImmediate), eax);
 | 
| +    if (!ReturnTrueFalseObject()) {
 | 
| +      __ Set(eax, Immediate(0));
 | 
| +    }
 | 
| +  }
 | 
| +  __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
 | 
|  
 | 
|    __ bind(&is_not_instance);
 | 
| -  __ IncrementCounter(&Counters::instance_of_stub_false, 1);
 | 
| -  __ Set(eax, Immediate(Smi::FromInt(1)));
 | 
| -  __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex));
 | 
| -  __ mov(Operand::StaticArray(scratch, times_pointer_size, roots_address), eax);
 | 
| -  __ ret((args_in_registers() ? 0 : 2) * kPointerSize);
 | 
| +  if (!HasCallSiteInlineCheck()) {
 | 
| +    __ Set(eax, Immediate(Smi::FromInt(1)));
 | 
| +    __ mov(scratch, Immediate(Heap::kInstanceofCacheAnswerRootIndex));
 | 
| +    __ mov(Operand::StaticArray(
 | 
| +        scratch, times_pointer_size, roots_address), eax);
 | 
| +  } else {
 | 
| +    // Get return address and delta to inlined map check.
 | 
| +    __ mov(eax, Factory::false_value());
 | 
| +    __ mov(scratch, Operand(esp, 0 * kPointerSize));
 | 
| +    __ sub(scratch, Operand(esp, 1 * kPointerSize));
 | 
| +    if (FLAG_debug_code) {
 | 
| +      __ cmpb(Operand(scratch, kDeltaToMov), kMovEaxImmediateByte);
 | 
| +      __ Assert(equal, "InstanceofStub unexpected call site cache (mov)");
 | 
| +    }
 | 
| +    __ mov(Operand(scratch, kDeltaToMovImmediate), eax);
 | 
| +    if (!ReturnTrueFalseObject()) {
 | 
| +      __ Set(eax, Immediate(Smi::FromInt(1)));
 | 
| +    }
 | 
| +  }
 | 
| +  __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
 | 
|  
 | 
|    Label object_not_null, object_not_null_or_smi;
 | 
|    __ bind(¬_js_object);
 | 
| @@ -5064,39 +5146,72 @@
 | 
|    // Null is not instance of anything.
 | 
|    __ cmp(object, Factory::null_value());
 | 
|    __ j(not_equal, &object_not_null);
 | 
| -  __ IncrementCounter(&Counters::instance_of_stub_false_null, 1);
 | 
|    __ Set(eax, Immediate(Smi::FromInt(1)));
 | 
| -  __ ret((args_in_registers() ? 0 : 2) * kPointerSize);
 | 
| +  __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
 | 
|  
 | 
|    __ bind(&object_not_null);
 | 
|    // Smi values is not instance of anything.
 | 
|    __ test(object, Immediate(kSmiTagMask));
 | 
|    __ j(not_zero, &object_not_null_or_smi, not_taken);
 | 
|    __ Set(eax, Immediate(Smi::FromInt(1)));
 | 
| -  __ ret((args_in_registers() ? 0 : 2) * kPointerSize);
 | 
| +  __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
 | 
|  
 | 
|    __ bind(&object_not_null_or_smi);
 | 
|    // String values is not instance of anything.
 | 
|    Condition is_string = masm->IsObjectStringType(object, scratch, scratch);
 | 
|    __ j(NegateCondition(is_string), &slow);
 | 
| -  __ IncrementCounter(&Counters::instance_of_stub_false_string, 1);
 | 
|    __ Set(eax, Immediate(Smi::FromInt(1)));
 | 
| -  __ ret((args_in_registers() ? 0 : 2) * kPointerSize);
 | 
| +  __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize);
 | 
|  
 | 
|    // Slow-case: Go through the JavaScript implementation.
 | 
|    __ bind(&slow);
 | 
| -  if (args_in_registers()) {
 | 
| +  if (HasArgsInRegisters()) {
 | 
|      // Push arguments below return address.
 | 
|      __ pop(scratch);
 | 
|      __ push(object);
 | 
|      __ push(function);
 | 
|      __ push(scratch);
 | 
|    }
 | 
| -  __ IncrementCounter(&Counters::instance_of_slow, 1);
 | 
|    __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
 | 
|  }
 | 
|  
 | 
|  
 | 
| +Register InstanceofStub::left() { return eax; }
 | 
| +
 | 
| +
 | 
| +Register InstanceofStub::right() { return edx; }
 | 
| +
 | 
| +
 | 
| +const char* InstanceofStub::GetName() {
 | 
| +  if (name_ != NULL) return name_;
 | 
| +  const int kMaxNameLength = 100;
 | 
| +  name_ = Bootstrapper::AllocateAutoDeletedArray(kMaxNameLength);
 | 
| +  if (name_ == NULL) return "OOM";
 | 
| +
 | 
| +  const char* args = "";
 | 
| +  if (HasArgsInRegisters()) {
 | 
| +    args = "_REGS";
 | 
| +  }
 | 
| +
 | 
| +  const char* inline_check = "";
 | 
| +  if (HasCallSiteInlineCheck()) {
 | 
| +    inline_check = "_INLINE";
 | 
| +  }
 | 
| +
 | 
| +  const char* return_true_false_object = "";
 | 
| +  if (ReturnTrueFalseObject()) {
 | 
| +    return_true_false_object = "_TRUEFALSE";
 | 
| +  }
 | 
| +
 | 
| +  OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
 | 
| +               "InstanceofStub%s%s%s",
 | 
| +               args,
 | 
| +               inline_check,
 | 
| +               return_true_false_object);
 | 
| +  return name_;
 | 
| +}
 | 
| +
 | 
| +
 | 
|  int CompareStub::MinorKey() {
 | 
|    // Encode the three parameters in a unique 16 bit value. To avoid duplicate
 | 
|    // stubs the never NaN NaN condition is only taken into account if the
 | 
| 
 |