void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
// -- esp[0] : return address
- // -- esp[4] : key
- // -- esp[8] : receiver
// -----------------------------------
Label slow, fast, array, extra, check_pixel_array;
- // Get the receiver from the stack.
- __ mov(edx, Operand(esp, 2 * kPointerSize)); // 2 ~ return address, key
// Check that the object isn't a smi.
__ test(edx, Immediate(kSmiTagMask));
__ j(zero, &slow, not_taken);
// Get the map from the receiver.
- __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
+ __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset));
// Check that the receiver does not require access checks. We need
// to do this because this generic stub does not perform map checks.
- __ movzx_b(ebx, FieldOperand(ecx, Map::kBitFieldOffset));
+ __ movzx_b(ebx, FieldOperand(edi, Map::kBitFieldOffset));
__ test(ebx, Immediate(1 << Map::kIsAccessCheckNeeded));
__ j(not_zero, &slow, not_taken);
- // Get the key from the stack.
- __ mov(ebx, Operand(esp, 1 * kPointerSize)); // 1 ~ return address
// Check that the key is a smi.
- __ test(ebx, Immediate(kSmiTagMask));
+ __ test(ecx, Immediate(kSmiTagMask));
__ j(not_zero, &slow, not_taken);
- // Get the instance type from the map of the receiver.
- __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
- // Check if the object is a JS array or not.
- __ cmp(ecx, JS_ARRAY_TYPE);
+ __ CmpInstanceType(edi, JS_ARRAY_TYPE);
__ j(equal, &array);
// Check that the object is some kind of JS object.
- __ cmp(ecx, FIRST_JS_OBJECT_TYPE);
- __ j(less, &slow, not_taken);
+ __ CmpInstanceType(edi, FIRST_JS_OBJECT_TYPE);
+ __ j(below, &slow, not_taken);
// Object case: Check key against length in the elements array.
// eax: value
// edx: JSObject
- // ebx: index (as a smi)
- __ mov(ecx, FieldOperand(edx, JSObject::kElementsOffset));
+ // ecx: key (a smi)
+ __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
// Check that the object is in fast mode (not dictionary).
- __ cmp(FieldOperand(ecx, HeapObject::kMapOffset),
- Immediate(Factory::fixed_array_map()));
- __ j(not_equal, &check_pixel_array, not_taken);
- // Untag the key (for checking against untagged length in the fixed array).
- __ mov(edx, Operand(ebx));
- __ sar(edx, kSmiTagSize); // untag the index and use it for the comparison
- __ cmp(edx, FieldOperand(ecx, Array::kLengthOffset));
- // eax: value
- // ecx: FixedArray
- // ebx: index (as a smi)
+ __ CheckMap(edi, Factory::fixed_array_map(), &check_pixel_array, true);
+ __ mov(ebx, Operand(ecx));
+ __ SmiUntag(ebx);
+ __ cmp(ebx, FieldOperand(edi, Array::kLengthOffset));
__ j(below, &fast, taken);
// Slow case: call runtime.
GenerateRuntimeSetProperty(masm);
// Check whether the elements is a pixel array.
- // eax: value
- // ecx: elements array
- // ebx: index (as a smi)
__ bind(&check_pixel_array);
- __ cmp(FieldOperand(ecx, HeapObject::kMapOffset),
- Immediate(Factory::pixel_array_map()));
- __ j(not_equal, &slow);
+ // eax: value
+ // ecx: key
+ // edx: receiver
+ // edi: elements array
+ __ CheckMap(edi, Factory::pixel_array_map(), &slow, true);
// Check that the value is a smi. If a conversion is needed call into the
// runtime to convert and clamp.
__ test(eax, Immediate(kSmiTagMask));
__ j(not_zero, &slow);
- __ sar(ebx, kSmiTagSize); // Untag the index.
- __ cmp(ebx, FieldOperand(ecx, PixelArray::kLengthOffset));
+ __ mov(ebx, ecx);
+ __ SmiUntag(ebx);
+ __ cmp(ebx, FieldOperand(edi, PixelArray::kLengthOffset));
__ j(above_equal, &slow);
- __ mov(edx, eax); // Save the value.
- __ sar(eax, kSmiTagSize); // Untag the value.
+ __ mov(ecx, eax); // Save the value. Key is not longer needed.
+ __ SmiUntag(ecx);
{ // Clamp the value to [0..255].
Label done;
- __ test(eax, Immediate(0xFFFFFF00));
+ __ test(ecx, Immediate(0xFFFFFF00));
__ j(zero, &done);
- __ setcc(negative, eax); // 1 if negative, 0 if positive.
- __ dec_b(eax); // 0 if negative, 255 if positive.
+ __ setcc(negative, ecx); // 1 if negative, 0 if positive.
+ __ dec_b(ecx); // 0 if negative, 255 if positive.
__ bind(&done);
}
- __ mov(ecx, FieldOperand(ecx, PixelArray::kExternalPointerOffset));
- __ mov_b(Operand(ecx, ebx, times_1, 0), eax);
- __ mov(eax, edx); // Return the original value.
- __ ret(0);
+ __ mov(edi, FieldOperand(edi, PixelArray::kExternalPointerOffset));
+ __ mov_b(Operand(edi, ebx, times_1, 0), ecx);
+ __ ret(0); // Return value in eax.
// Extra capacity case: Check if there is extra capacity to
// perform the store and update the length. Used for adding one
// element to the array by writing to array[array.length].
__ bind(&extra);
// eax: value
- // edx: JSArray
- // ecx: FixedArray
- // ebx: index (as a smi)
- // flags: compare (ebx, edx.length())
+ // edx: receiver, a JSArray
+ // ecx: key, a smi.
+ // edi: receiver->elements, a FixedArray
+ // flags: compare (ecx, edx.length())
__ j(not_equal, &slow, not_taken); // do not leave holes in the array
- __ sar(ebx, kSmiTagSize); // untag
- __ cmp(ebx, FieldOperand(ecx, Array::kLengthOffset));
+ __ mov(ebx, ecx);
+ __ SmiUntag(ebx); // untag
+ __ cmp(ebx, FieldOperand(edi, Array::kLengthOffset));
__ j(above_equal, &slow, not_taken);
- // Restore tag and increment.
- __ lea(ebx, Operand(ebx, times_2, 1 << kSmiTagSize));
- __ mov(FieldOperand(edx, JSArray::kLengthOffset), ebx);
- __ sub(Operand(ebx), Immediate(1 << kSmiTagSize)); // decrement ebx again
+ // Add 1 to receiver->length, and go to fast array write.
+ __ add(FieldOperand(edx, JSArray::kLengthOffset),
+ Immediate(1 << kSmiTagSize));
__ jmp(&fast);
// Array case: Get the length and the elements array from the JS
// length is always a smi.
__ bind(&array);
// eax: value
- // edx: JSArray
- // ebx: index (as a smi)
- __ mov(ecx, FieldOperand(edx, JSObject::kElementsOffset));
- __ cmp(FieldOperand(ecx, HeapObject::kMapOffset),
- Immediate(Factory::fixed_array_map()));
- __ j(not_equal, &check_pixel_array);
+ // edx: receiver, a JSArray
+ // ecx: key, a smi.
+ __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
+ __ CheckMap(edi, Factory::fixed_array_map(), &check_pixel_array, true);
// Check the key against the length in the array, compute the
// address to store into and fall through to fast case.
- __ cmp(ebx, FieldOperand(edx, JSArray::kLengthOffset));
+ __ cmp(ecx, FieldOperand(edx, JSArray::kLengthOffset)); // Compare smis.
__ j(above_equal, &extra, not_taken);
// Fast case: Do the store.
__ bind(&fast);
// eax: value
- // ecx: FixedArray
- // ebx: index (as a smi)
- __ mov(Operand(ecx, ebx, times_2, FixedArray::kHeaderSize - kHeapObjectTag),
- eax);
+ // ecx: key (a smi)
+ // edx: receiver
+ // edi: FixedArray receiver->elements
+ __ mov(FieldOperand(edi, ecx, times_2, FixedArray::kHeaderSize), eax);
// Update write barrier for the elements array address.
__ mov(edx, Operand(eax));
- __ RecordWrite(ecx, 0, edx, ebx);
+ __ RecordWrite(edi, 0, edx, ecx);
__ ret(0);
}
ExternalArrayType array_type) {
// ----------- S t a t e -------------
// -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
// -- esp[0] : return address
- // -- esp[4] : key
- // -- esp[8] : receiver
// -----------------------------------
Label slow, check_heap_number;
- // Get the receiver from the stack.
- __ mov(edx, Operand(esp, 2 * kPointerSize));
// Check that the object isn't a smi.
__ test(edx, Immediate(kSmiTagMask));
__ j(zero, &slow);
// Get the map from the receiver.
- __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
+ __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset));
// Check that the receiver does not require access checks. We need
// to do this because this generic stub does not perform map checks.
- __ movzx_b(ebx, FieldOperand(ecx, Map::kBitFieldOffset));
+ __ movzx_b(ebx, FieldOperand(edi, Map::kBitFieldOffset));
__ test(ebx, Immediate(1 << Map::kIsAccessCheckNeeded));
__ j(not_zero, &slow);
- // Get the key from the stack.
- __ mov(ebx, Operand(esp, 1 * kPointerSize)); // 1 ~ return address
// Check that the key is a smi.
- __ test(ebx, Immediate(kSmiTagMask));
+ __ test(ecx, Immediate(kSmiTagMask));
__ j(not_zero, &slow);
// Get the instance type from the map of the receiver.
- __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
- // Check that the object is a JS object.
- __ cmp(ecx, JS_OBJECT_TYPE);
+ __ CmpInstanceType(edi, JS_OBJECT_TYPE);
__ j(not_equal, &slow);
// Check that the elements array is the appropriate type of
// ExternalArray.
// eax: value
- // edx: JSObject
- // ebx: index (as a smi)
- __ mov(ecx, FieldOperand(edx, JSObject::kElementsOffset));
- Handle<Map> map(Heap::MapForExternalArrayType(array_type));
- __ cmp(FieldOperand(ecx, HeapObject::kMapOffset),
- Immediate(map));
- __ j(not_equal, &slow);
+ // edx: receiver, a JSObject
+ // ecx: key, a smi
+ __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
+ __ CheckMap(edi, Handle<Map>(Heap::MapForExternalArrayType(array_type)),
+ &slow, true);
// Check that the index is in range.
- __ sar(ebx, kSmiTagSize); // Untag the index.
- __ cmp(ebx, FieldOperand(ecx, ExternalArray::kLengthOffset));
+ __ mov(ebx, ecx);
+ __ SmiUntag(ebx);
+ __ cmp(ebx, FieldOperand(edi, ExternalArray::kLengthOffset));
// Unsigned comparison catches both negative and too-large values.
__ j(above_equal, &slow);
// Handle both smis and HeapNumbers in the fast path. Go to the
// runtime for all other kinds of values.
// eax: value
- // ecx: elements array
+ // edx: receiver
+ // ecx: key
+ // edi: elements array
// ebx: untagged index
__ test(eax, Immediate(kSmiTagMask));
__ j(not_equal, &check_heap_number);
// smi case
- __ mov(edx, eax); // Save the value.
- __ sar(eax, kSmiTagSize); // Untag the value.
- __ mov(ecx, FieldOperand(ecx, ExternalArray::kExternalPointerOffset));
+ __ mov(ecx, eax); // Preserve the value in eax. Key is no longer needed.
+ __ SmiUntag(ecx);
+ __ mov(edi, FieldOperand(edi, ExternalArray::kExternalPointerOffset));
// ecx: base pointer of external storage
switch (array_type) {
case kExternalByteArray:
case kExternalUnsignedByteArray:
- __ mov_b(Operand(ecx, ebx, times_1, 0), eax);
+ __ mov_b(Operand(edi, ebx, times_1, 0), ecx);
break;
case kExternalShortArray:
case kExternalUnsignedShortArray:
- __ mov_w(Operand(ecx, ebx, times_2, 0), eax);
+ __ mov_w(Operand(edi, ebx, times_2, 0), ecx);
break;
case kExternalIntArray:
case kExternalUnsignedIntArray:
- __ mov(Operand(ecx, ebx, times_4, 0), eax);
+ __ mov(Operand(edi, ebx, times_4, 0), ecx);
break;
case kExternalFloatArray:
// Need to perform int-to-float conversion.
- __ push(eax);
+ __ push(ecx);
__ fild_s(Operand(esp, 0));
- __ pop(eax);
- __ fstp_s(Operand(ecx, ebx, times_4, 0));
+ __ pop(ecx);
+ __ fstp_s(Operand(edi, ebx, times_4, 0));
break;
default:
UNREACHABLE();
break;
}
- __ mov(eax, edx); // Return the original value.
- __ ret(0);
+ __ ret(0); // Return the original value.
__ bind(&check_heap_number);
+ // eax: value
+ // edx: receiver
+ // ecx: key
+ // edi: elements array
+ // ebx: untagged index
__ cmp(FieldOperand(eax, HeapObject::kMapOffset),
Immediate(Factory::heap_number_map()));
__ j(not_equal, &slow);
// +/-Infinity into integer arrays basically undefined. For more
// reproducible behavior, convert these to zero.
__ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
- __ mov(edx, eax); // Save the value.
- __ mov(ecx, FieldOperand(ecx, ExternalArray::kExternalPointerOffset));
+ __ mov(edi, FieldOperand(edi, ExternalArray::kExternalPointerOffset));
// ebx: untagged index
- // ecx: base pointer of external storage
+ // edi: base pointer of external storage
// top of FPU stack: value
if (array_type == kExternalFloatArray) {
- __ fstp_s(Operand(ecx, ebx, times_4, 0));
- __ mov(eax, edx); // Return the original value.
+ __ fstp_s(Operand(edi, ebx, times_4, 0));
__ ret(0);
} else {
// Need to perform float-to-int conversion.
__ j(parity_even, &is_nan);
if (array_type != kExternalUnsignedIntArray) {
- __ push(eax); // Make room on stack
+ __ push(ecx); // Make room on stack
__ fistp_s(Operand(esp, 0));
- __ pop(eax);
+ __ pop(ecx);
} else {
// fistp stores values as signed integers.
// To represent the entire range, we need to store as a 64-bit
// int and discard the high 32 bits.
- __ push(eax); // Make room on stack
- __ push(eax); // Make room on stack
+ __ sub(Operand(esp), Immediate(2 * kPointerSize));
__ fistp_d(Operand(esp, 0));
- __ pop(eax);
- __ mov(Operand(esp, 0), eax);
- __ pop(eax);
+ __ pop(ecx);
+ __ add(Operand(esp), Immediate(kPointerSize));
}
- // eax: untagged integer value
+ // ecx: untagged integer value
switch (array_type) {
case kExternalByteArray:
case kExternalUnsignedByteArray:
- __ mov_b(Operand(ecx, ebx, times_1, 0), eax);
+ __ mov_b(Operand(edi, ebx, times_1, 0), ecx);
break;
case kExternalShortArray:
case kExternalUnsignedShortArray:
- __ mov_w(Operand(ecx, ebx, times_2, 0), eax);
+ __ mov_w(Operand(edi, ebx, times_2, 0), ecx);
break;
case kExternalIntArray:
case kExternalUnsignedIntArray: {
// This test would apparently detect both NaN and Infinity,
// but we've already checked for NaN using the FPU hardware
// above.
- __ mov_w(edi, FieldOperand(edx, HeapNumber::kValueOffset + 6));
- __ and_(edi, 0x7FF0);
- __ cmp(edi, 0x7FF0);
+ __ mov_w(edx, FieldOperand(eax, HeapNumber::kValueOffset + 6));
+ __ and_(edx, 0x7FF0);
+ __ cmp(edx, 0x7FF0);
__ j(not_equal, ¬_infinity);
- __ mov(eax, 0);
+ __ mov(ecx, 0);
__ bind(¬_infinity);
- __ mov(Operand(ecx, ebx, times_4, 0), eax);
+ __ mov(Operand(edi, ebx, times_4, 0), ecx);
break;
}
default:
UNREACHABLE();
break;
}
- __ mov(eax, edx); // Return the original value.
- __ ret(0);
+ __ ret(0); // Return original value.
__ bind(&is_nan);
__ ffree();
switch (array_type) {
case kExternalByteArray:
case kExternalUnsignedByteArray:
- __ mov_b(Operand(ecx, ebx, times_1, 0), 0);
+ __ mov_b(Operand(edi, ebx, times_1, 0), 0);
break;
case kExternalShortArray:
case kExternalUnsignedShortArray:
- __ mov(eax, 0);
- __ mov_w(Operand(ecx, ebx, times_2, 0), eax);
+ __ xor_(ecx, Operand(ecx));
+ __ mov_w(Operand(edi, ebx, times_2, 0), ecx);
break;
case kExternalIntArray:
case kExternalUnsignedIntArray:
- __ mov(Operand(ecx, ebx, times_4, 0), Immediate(0));
+ __ mov(Operand(edi, ebx, times_4, 0), Immediate(0));
break;
default:
UNREACHABLE();
break;
}
- __ mov(eax, edx); // Return the original value.
- __ ret(0);
+ __ ret(0); // Return the original value.
}
// Slow case: call runtime.
void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
// -- esp[0] : return address
- // -- esp[4] : key
- // -- esp[8] : receiver
// -----------------------------------
- __ pop(ecx);
- __ push(Operand(esp, 1 * kPointerSize));
- __ push(Operand(esp, 1 * kPointerSize));
- __ push(eax);
+ __ pop(ebx);
+ __ push(edx);
__ push(ecx);
+ __ push(eax);
+ __ push(ebx);
// Do tail-call to runtime routine.
__ TailCallRuntime(Runtime::kSetProperty, 3, 1);
void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : value
+ // -- ecx : key
+ // -- edx : receiver
// -- esp[0] : return address
- // -- esp[4] : key
- // -- esp[8] : receiver
// -----------------------------------
- __ pop(ecx);
- __ push(Operand(esp, 1 * kPointerSize));
- __ push(Operand(esp, 1 * kPointerSize));
- __ push(eax);
+ __ pop(ebx);
+ __ push(edx);
__ push(ecx);
+ __ push(eax);
+ __ push(ebx);
// Do tail-call to runtime routine.
ExternalReference ref = ExternalReference(IC_Utility(kKeyedStoreIC_Miss));
}
+// This function assumes that the only results that could be in a_reg or b_reg
+// are a and b. Other results can be live, but must not be in a_reg or b_reg.
+void VirtualFrame::MoveResultsToRegisters(Result* a,
+ Result* b,
+ Register a_reg,
+ Register b_reg) {
+ if (a->is_register() && a->reg().is(a_reg)) {
+ b->ToRegister(b_reg);
+ } else if (!cgen()->allocator()->is_used(a_reg)) {
+ a->ToRegister(a_reg);
+ b->ToRegister(b_reg);
+ } else if (cgen()->allocator()->is_used(b_reg)) {
+ // a must be in b_reg, b in a_reg.
+ __ xchg(a_reg, b_reg);
+ // Results a and b will be invalidated, so it is ok if they are switched.
+ } else {
+ b->ToRegister(b_reg);
+ a->ToRegister(a_reg);
+ }
+ a->Unuse();
+ b->Unuse();
+}
+
+
Result VirtualFrame::CallLoadIC(RelocInfo::Mode mode) {
// Name and receiver are on the top of the frame. The IC expects
// name in ecx and receiver in eax.
- Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
Result name = Pop();
Result receiver = Pop();
PrepareForCall(0, 0); // No stack arguments.
- // Move results to the right registers:
- if (name.is_register() && name.reg().is(eax)) {
- if (receiver.is_register() && receiver.reg().is(ecx)) {
- // Wrong registers.
- __ xchg(eax, ecx);
- } else {
- // Register ecx is free for name, which frees eax for receiver.
- name.ToRegister(ecx);
- receiver.ToRegister(eax);
- }
- } else {
- // Register eax is free for receiver, which frees ecx for name.
- receiver.ToRegister(eax);
- name.ToRegister(ecx);
- }
- name.Unuse();
- receiver.Unuse();
+ MoveResultsToRegisters(&name, &receiver, ecx, eax);
+
+ Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
return RawCallCodeObject(ic, mode);
}
Result key = Pop();
Result receiver = Pop();
PrepareForCall(0, 0);
-
- if (!key.is_register() || !key.reg().is(edx)) {
- // Register edx is available for receiver.
- receiver.ToRegister(edx);
- key.ToRegister(eax);
- } else if (!receiver.is_register() || !receiver.reg().is(eax)) {
- // Register eax is available for key.
- key.ToRegister(eax);
- receiver.ToRegister(edx);
- } else {
- __ xchg(edx, eax);
- }
- key.Unuse();
- receiver.Unuse();
+ MoveResultsToRegisters(&key, &receiver, eax, edx);
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
return RawCallCodeObject(ic, mode);
value.ToRegister(eax);
__ mov(edx, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
__ mov(ecx, name);
+ value.Unuse();
} else {
Result receiver = Pop();
PrepareForCall(0, 0);
-
- if (value.is_register() && value.reg().is(edx)) {
- if (receiver.is_register() && receiver.reg().is(eax)) {
- // Wrong registers.
- __ xchg(eax, edx);
- } else {
- // Register eax is free for value, which frees edx for receiver.
- value.ToRegister(eax);
- receiver.ToRegister(edx);
- }
- } else {
- // Register edx is free for receiver, which guarantees eax is free for
- // value.
- receiver.ToRegister(edx);
- value.ToRegister(eax);
- }
+ MoveResultsToRegisters(&value, &receiver, eax, edx);
}
__ mov(ecx, name);
- value.Unuse();
return RawCallCodeObject(ic, RelocInfo::CODE_TARGET);
}
Result VirtualFrame::CallKeyedStoreIC() {
// Value, key, and receiver are on the top of the frame. The IC
- // expects value in eax and key and receiver on the stack. It does
- // not drop the key and receiver.
- Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
+ // expects value in eax, key in ecx, and receiver in edx.
Result value = Pop();
- PrepareForCall(2, 0); // Two stack args, neither callee-dropped.
- value.ToRegister(eax);
- value.Unuse();
+ Result key = Pop();
+ Result receiver = Pop();
+ PrepareForCall(0, 0);
+ if (!cgen()->allocator()->is_used(eax) ||
+ (value.is_register() && value.reg().is(eax))) {
+ value.ToRegister(eax); // No effect if value is in eax already.
+ MoveResultsToRegisters(&key, &receiver, ecx, edx);
+ value.Unuse();
+ } else if (!cgen()->allocator()->is_used(ecx) ||
+ (key.is_register() && key.reg().is(ecx))) {
+ // Receiver and/or key are in eax.
+ key.ToRegister(ecx);
+ MoveResultsToRegisters(&value, &receiver, eax, edx);
+ key.Unuse();
+ } else if (!cgen()->allocator()->is_used(edx) ||
+ (receiver.is_register() && receiver.reg().is(edx))) {
+ receiver.ToRegister(edx);
+ MoveResultsToRegisters(&key, &value, ecx, eax);
+ receiver.Unuse();
+ } else {
+ // All three registers are used, and no value is in the correct place.
+ // We have one of the two circular permutations of eax, ecx, edx.
+ ASSERT(value.is_register());
+ if (value.reg().is(ecx)) {
+ __ xchg(eax, edx);
+ __ xchg(eax, ecx);
+ } else {
+ __ xchg(eax, ecx);
+ __ xchg(eax, edx);
+ }
+ value.Unuse();
+ key.Unuse();
+ receiver.Unuse();
+ }
+
+ Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
return RawCallCodeObject(ic, RelocInfo::CODE_TARGET);
}