Index: src/ia32/code-stubs-ia32.cc |
diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc |
index bd95c8d0ba8e5bcc856d2b0849667a3a7d5064d2..91fb050c7a725e7eeebb518eb7a71837b86f3575 100644 |
--- a/src/ia32/code-stubs-ia32.cc |
+++ b/src/ia32/code-stubs-ia32.cc |
@@ -1772,7 +1772,6 @@ void TypeRecordingBinaryOpStub::GenerateSmiStub(MacroAssembler* masm) { |
} |
- |
void TypeRecordingBinaryOpStub::GenerateStringStub(MacroAssembler* masm) { |
Label call_runtime; |
ASSERT(operands_type_ == TRBinaryOpIC::STRING); |
@@ -4973,7 +4972,26 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { |
} |
+// 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 +4999,22 @@ void InstanceofStub::Generate(MacroAssembler* masm) { |
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 = BitCast<int8_t, uint8_t>(0x81); |
+ static const int8_t kCmpEdiImmediateByte2 = BitCast<int8_t, uint8_t>(0xff); |
+ static const int8_t kMovEaxImmediateByte = BitCast<int8_t, uint8_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 +5024,26 @@ void InstanceofStub::Generate(MacroAssembler* masm) { |
__ 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 +5052,29 @@ void InstanceofStub::Generate(MacroAssembler* masm) { |
__ 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 +5090,48 @@ void InstanceofStub::Generate(MacroAssembler* masm) { |
__ 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 +5145,61 @@ void InstanceofStub::Generate(MacroAssembler* masm) { |
// 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()) { |
- // Push arguments below return address. |
- __ pop(scratch); |
+ if (!ReturnTrueFalseObject()) { |
+ // Tail call the builtin which returns 0 or 1. |
+ if (HasArgsInRegisters()) { |
+ // Push arguments below return address. |
+ __ pop(scratch); |
+ __ push(object); |
+ __ push(function); |
+ __ push(scratch); |
+ } |
+ __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION); |
+ } else { |
+ // Call the builtin and convert 0/1 to true/false. |
+ __ EnterInternalFrame(); |
__ push(object); |
__ push(function); |
- __ push(scratch); |
+ __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION); |
+ __ LeaveInternalFrame(); |
+ NearLabel true_value, done; |
+ __ test(eax, Operand(eax)); |
+ __ j(zero, &true_value); |
+ __ mov(eax, Factory::false_value()); |
+ __ jmp(&done); |
+ __ bind(&true_value); |
+ __ mov(eax, Factory::true_value()); |
+ __ bind(&done); |
+ __ ret((HasArgsInRegisters() ? 0 : 2) * kPointerSize); |
} |
- __ IncrementCounter(&Counters::instance_of_slow, 1); |
- __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION); |
} |
+Register InstanceofStub::left() { return eax; } |
+ |
+ |
+Register InstanceofStub::right() { return edx; } |
+ |
+ |
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 |