Chromium Code Reviews| Index: src/ia32/code-stubs-ia32.cc |
| =================================================================== |
| --- src/ia32/code-stubs-ia32.cc (revision 6109) |
| +++ 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 kMovEaxImmediate = static_cast<int8_t>(0xb8); |
|
Mads Ager (chromium)
2011/01/05 09:59:26
Add the 'Byte' suffix to this one as well?
Søren Thygesen Gjesse
2011/01/05 11:11:01
Done.
|
| + |
| + 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), kMovEaxImmediate); |
| + __ 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), kMovEaxImmediate); |
| + __ 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 |