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 |