Upgrade to V8 3.3
Merge V8 at 3.3.10.39
Simple merge required updates to makefiles only.
Bug: 5688872
Change-Id: I14703f418235f5ce6013b9b3e2e502407a9f6dfd
diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc
index c365385..7075e66 100644
--- a/src/x64/code-stubs-x64.cc
+++ b/src/x64/code-stubs-x64.cc
@@ -40,15 +40,15 @@
void ToNumberStub::Generate(MacroAssembler* masm) {
// The ToNumber stub takes one argument in eax.
- NearLabel check_heap_number, call_builtin;
+ Label check_heap_number, call_builtin;
__ SmiTest(rax);
- __ j(not_zero, &check_heap_number);
+ __ j(not_zero, &check_heap_number, Label::kNear);
__ Ret();
__ bind(&check_heap_number);
__ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
Heap::kHeapNumberMapRootIndex);
- __ j(not_equal, &call_builtin);
+ __ j(not_equal, &call_builtin, Label::kNear);
__ Ret();
__ bind(&call_builtin);
@@ -232,12 +232,28 @@
void ToBooleanStub::Generate(MacroAssembler* masm) {
- NearLabel false_result, true_result, not_string;
+ Label false_result, true_result, not_string;
__ movq(rax, Operand(rsp, 1 * kPointerSize));
+ // undefined -> false
+ __ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
+ __ j(equal, &false_result);
+
+ // Boolean -> its value
+ __ CompareRoot(rax, Heap::kFalseValueRootIndex);
+ __ j(equal, &false_result);
+ __ CompareRoot(rax, Heap::kTrueValueRootIndex);
+ __ j(equal, &true_result);
+
+ // Smis: 0 -> false, all other -> true
+ __ Cmp(rax, Smi::FromInt(0));
+ __ j(equal, &false_result);
+ Condition is_smi = __ CheckSmi(rax);
+ __ j(is_smi, &true_result);
+
// 'null' => false.
__ CompareRoot(rax, Heap::kNullValueRootIndex);
- __ j(equal, &false_result);
+ __ j(equal, &false_result, Label::kNear);
// Get the map and type of the heap object.
// We don't use CmpObjectType because we manipulate the type field.
@@ -247,28 +263,28 @@
// Undetectable => false.
__ movzxbq(rbx, FieldOperand(rdx, Map::kBitFieldOffset));
__ and_(rbx, Immediate(1 << Map::kIsUndetectable));
- __ j(not_zero, &false_result);
+ __ j(not_zero, &false_result, Label::kNear);
// JavaScript object => true.
__ cmpq(rcx, Immediate(FIRST_JS_OBJECT_TYPE));
- __ j(above_equal, &true_result);
+ __ j(above_equal, &true_result, Label::kNear);
// String value => false iff empty.
__ cmpq(rcx, Immediate(FIRST_NONSTRING_TYPE));
- __ j(above_equal, ¬_string);
+ __ j(above_equal, ¬_string, Label::kNear);
__ movq(rdx, FieldOperand(rax, String::kLengthOffset));
__ SmiTest(rdx);
- __ j(zero, &false_result);
- __ jmp(&true_result);
+ __ j(zero, &false_result, Label::kNear);
+ __ jmp(&true_result, Label::kNear);
__ bind(¬_string);
__ CompareRoot(rdx, Heap::kHeapNumberMapRootIndex);
- __ j(not_equal, &true_result);
+ __ j(not_equal, &true_result, Label::kNear);
// HeapNumber => false iff +0, -0, or NaN.
// These three cases set the zero flag when compared to zero using ucomisd.
- __ xorpd(xmm0, xmm0);
+ __ xorps(xmm0, xmm0);
__ ucomisd(xmm0, FieldOperand(rax, HeapNumber::kValueOffset));
- __ j(zero, &false_result);
+ __ j(zero, &false_result, Label::kNear);
// Fall through to |true_result|.
// Return 1/0 for true/false in rax.
@@ -322,15 +338,354 @@
};
-Handle<Code> GetTypeRecordingBinaryOpStub(int key,
- TRBinaryOpIC::TypeInfo type_info,
- TRBinaryOpIC::TypeInfo result_type_info) {
- TypeRecordingBinaryOpStub stub(key, type_info, result_type_info);
+// Get the integer part of a heap number.
+// Overwrites the contents of rdi, rbx and rcx. Result cannot be rdi or rbx.
+void IntegerConvert(MacroAssembler* masm,
+ Register result,
+ Register source) {
+ // Result may be rcx. If result and source are the same register, source will
+ // be overwritten.
+ ASSERT(!result.is(rdi) && !result.is(rbx));
+ // TODO(lrn): When type info reaches here, if value is a 32-bit integer, use
+ // cvttsd2si (32-bit version) directly.
+ Register double_exponent = rbx;
+ Register double_value = rdi;
+ Label done, exponent_63_plus;
+ // Get double and extract exponent.
+ __ movq(double_value, FieldOperand(source, HeapNumber::kValueOffset));
+ // Clear result preemptively, in case we need to return zero.
+ __ xorl(result, result);
+ __ movq(xmm0, double_value); // Save copy in xmm0 in case we need it there.
+ // Double to remove sign bit, shift exponent down to least significant bits.
+ // and subtract bias to get the unshifted, unbiased exponent.
+ __ lea(double_exponent, Operand(double_value, double_value, times_1, 0));
+ __ shr(double_exponent, Immediate(64 - HeapNumber::kExponentBits));
+ __ subl(double_exponent, Immediate(HeapNumber::kExponentBias));
+ // Check whether the exponent is too big for a 63 bit unsigned integer.
+ __ cmpl(double_exponent, Immediate(63));
+ __ j(above_equal, &exponent_63_plus, Label::kNear);
+ // Handle exponent range 0..62.
+ __ cvttsd2siq(result, xmm0);
+ __ jmp(&done, Label::kNear);
+
+ __ bind(&exponent_63_plus);
+ // Exponent negative or 63+.
+ __ cmpl(double_exponent, Immediate(83));
+ // If exponent negative or above 83, number contains no significant bits in
+ // the range 0..2^31, so result is zero, and rcx already holds zero.
+ __ j(above, &done, Label::kNear);
+
+ // Exponent in rage 63..83.
+ // Mantissa * 2^exponent contains bits in the range 2^0..2^31, namely
+ // the least significant exponent-52 bits.
+
+ // Negate low bits of mantissa if value is negative.
+ __ addq(double_value, double_value); // Move sign bit to carry.
+ __ sbbl(result, result); // And convert carry to -1 in result register.
+ // if scratch2 is negative, do (scratch2-1)^-1, otherwise (scratch2-0)^0.
+ __ addl(double_value, result);
+ // Do xor in opposite directions depending on where we want the result
+ // (depending on whether result is rcx or not).
+
+ if (result.is(rcx)) {
+ __ xorl(double_value, result);
+ // Left shift mantissa by (exponent - mantissabits - 1) to save the
+ // bits that have positional values below 2^32 (the extra -1 comes from the
+ // doubling done above to move the sign bit into the carry flag).
+ __ leal(rcx, Operand(double_exponent, -HeapNumber::kMantissaBits - 1));
+ __ shll_cl(double_value);
+ __ movl(result, double_value);
+ } else {
+ // As the then-branch, but move double-value to result before shifting.
+ __ xorl(result, double_value);
+ __ leal(rcx, Operand(double_exponent, -HeapNumber::kMantissaBits - 1));
+ __ shll_cl(result);
+ }
+
+ __ bind(&done);
+}
+
+
+Handle<Code> GetUnaryOpStub(int key, UnaryOpIC::TypeInfo type_info) {
+ UnaryOpStub stub(key, type_info);
return stub.GetCode();
}
-void TypeRecordingBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
+void UnaryOpStub::Generate(MacroAssembler* masm) {
+ switch (operand_type_) {
+ case UnaryOpIC::UNINITIALIZED:
+ GenerateTypeTransition(masm);
+ break;
+ case UnaryOpIC::SMI:
+ GenerateSmiStub(masm);
+ break;
+ case UnaryOpIC::HEAP_NUMBER:
+ GenerateHeapNumberStub(masm);
+ break;
+ case UnaryOpIC::GENERIC:
+ GenerateGenericStub(masm);
+ break;
+ }
+}
+
+
+void UnaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
+ __ pop(rcx); // Save return address.
+ __ push(rax);
+ // Left and right arguments are now on top.
+ // Push this stub's key. Although the operation and the type info are
+ // encoded into the key, the encoding is opaque, so push them too.
+ __ Push(Smi::FromInt(MinorKey()));
+ __ Push(Smi::FromInt(op_));
+ __ Push(Smi::FromInt(operand_type_));
+
+ __ push(rcx); // Push return address.
+
+ // Patch the caller to an appropriate specialized stub and return the
+ // operation result to the caller of the stub.
+ __ TailCallExternalReference(
+ ExternalReference(IC_Utility(IC::kUnaryOp_Patch),
+ masm->isolate()),
+ 4,
+ 1);
+}
+
+
+// TODO(svenpanne): Use virtual functions instead of switch.
+void UnaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
+ switch (op_) {
+ case Token::SUB:
+ GenerateSmiStubSub(masm);
+ break;
+ case Token::BIT_NOT:
+ GenerateSmiStubBitNot(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void UnaryOpStub::GenerateSmiStubSub(MacroAssembler* masm) {
+ Label slow;
+ GenerateSmiCodeSub(masm, &slow, &slow, Label::kNear, Label::kNear);
+ __ bind(&slow);
+ GenerateTypeTransition(masm);
+}
+
+
+void UnaryOpStub::GenerateSmiStubBitNot(MacroAssembler* masm) {
+ Label non_smi;
+ GenerateSmiCodeBitNot(masm, &non_smi, Label::kNear);
+ __ bind(&non_smi);
+ GenerateTypeTransition(masm);
+}
+
+
+void UnaryOpStub::GenerateSmiCodeSub(MacroAssembler* masm,
+ Label* non_smi,
+ Label* slow,
+ Label::Distance non_smi_near,
+ Label::Distance slow_near) {
+ Label done;
+ __ JumpIfNotSmi(rax, non_smi, non_smi_near);
+ __ SmiNeg(rax, rax, &done, Label::kNear);
+ __ jmp(slow, slow_near);
+ __ bind(&done);
+ __ ret(0);
+}
+
+
+void UnaryOpStub::GenerateSmiCodeBitNot(MacroAssembler* masm,
+ Label* non_smi,
+ Label::Distance non_smi_near) {
+ __ JumpIfNotSmi(rax, non_smi, non_smi_near);
+ __ SmiNot(rax, rax);
+ __ ret(0);
+}
+
+
+// TODO(svenpanne): Use virtual functions instead of switch.
+void UnaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
+ switch (op_) {
+ case Token::SUB:
+ GenerateHeapNumberStubSub(masm);
+ break;
+ case Token::BIT_NOT:
+ GenerateHeapNumberStubBitNot(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void UnaryOpStub::GenerateHeapNumberStubSub(MacroAssembler* masm) {
+ Label non_smi, slow, call_builtin;
+ GenerateSmiCodeSub(masm, &non_smi, &call_builtin, Label::kNear);
+ __ bind(&non_smi);
+ GenerateHeapNumberCodeSub(masm, &slow);
+ __ bind(&slow);
+ GenerateTypeTransition(masm);
+ __ bind(&call_builtin);
+ GenerateGenericCodeFallback(masm);
+}
+
+
+void UnaryOpStub::GenerateHeapNumberStubBitNot(
+ MacroAssembler* masm) {
+ Label non_smi, slow;
+ GenerateSmiCodeBitNot(masm, &non_smi, Label::kNear);
+ __ bind(&non_smi);
+ GenerateHeapNumberCodeBitNot(masm, &slow);
+ __ bind(&slow);
+ GenerateTypeTransition(masm);
+}
+
+
+void UnaryOpStub::GenerateHeapNumberCodeSub(MacroAssembler* masm,
+ Label* slow) {
+ // Check if the operand is a heap number.
+ __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
+ Heap::kHeapNumberMapRootIndex);
+ __ j(not_equal, slow);
+
+ // Operand is a float, negate its value by flipping the sign bit.
+ if (mode_ == UNARY_OVERWRITE) {
+ __ Set(kScratchRegister, 0x01);
+ __ shl(kScratchRegister, Immediate(63));
+ __ xor_(FieldOperand(rax, HeapNumber::kValueOffset), kScratchRegister);
+ } else {
+ // Allocate a heap number before calculating the answer,
+ // so we don't have an untagged double around during GC.
+ Label slow_allocate_heapnumber, heapnumber_allocated;
+ __ AllocateHeapNumber(rcx, rbx, &slow_allocate_heapnumber);
+ __ jmp(&heapnumber_allocated);
+
+ __ bind(&slow_allocate_heapnumber);
+ __ EnterInternalFrame();
+ __ push(rax);
+ __ CallRuntime(Runtime::kNumberAlloc, 0);
+ __ movq(rcx, rax);
+ __ pop(rax);
+ __ LeaveInternalFrame();
+ __ bind(&heapnumber_allocated);
+ // rcx: allocated 'empty' number
+
+ // Copy the double value to the new heap number, flipping the sign.
+ __ movq(rdx, FieldOperand(rax, HeapNumber::kValueOffset));
+ __ Set(kScratchRegister, 0x01);
+ __ shl(kScratchRegister, Immediate(63));
+ __ xor_(rdx, kScratchRegister); // Flip sign.
+ __ movq(FieldOperand(rcx, HeapNumber::kValueOffset), rdx);
+ __ movq(rax, rcx);
+ }
+ __ ret(0);
+}
+
+
+void UnaryOpStub::GenerateHeapNumberCodeBitNot(MacroAssembler* masm,
+ Label* slow) {
+ // Check if the operand is a heap number.
+ __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
+ Heap::kHeapNumberMapRootIndex);
+ __ j(not_equal, slow);
+
+ // Convert the heap number in rax to an untagged integer in rcx.
+ IntegerConvert(masm, rax, rax);
+
+ // Do the bitwise operation and smi tag the result.
+ __ notl(rax);
+ __ Integer32ToSmi(rax, rax);
+ __ ret(0);
+}
+
+
+// TODO(svenpanne): Use virtual functions instead of switch.
+void UnaryOpStub::GenerateGenericStub(MacroAssembler* masm) {
+ switch (op_) {
+ case Token::SUB:
+ GenerateGenericStubSub(masm);
+ break;
+ case Token::BIT_NOT:
+ GenerateGenericStubBitNot(masm);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void UnaryOpStub::GenerateGenericStubSub(MacroAssembler* masm) {
+ Label non_smi, slow;
+ GenerateSmiCodeSub(masm, &non_smi, &slow, Label::kNear);
+ __ bind(&non_smi);
+ GenerateHeapNumberCodeSub(masm, &slow);
+ __ bind(&slow);
+ GenerateGenericCodeFallback(masm);
+}
+
+
+void UnaryOpStub::GenerateGenericStubBitNot(MacroAssembler* masm) {
+ Label non_smi, slow;
+ GenerateSmiCodeBitNot(masm, &non_smi, Label::kNear);
+ __ bind(&non_smi);
+ GenerateHeapNumberCodeBitNot(masm, &slow);
+ __ bind(&slow);
+ GenerateGenericCodeFallback(masm);
+}
+
+
+void UnaryOpStub::GenerateGenericCodeFallback(MacroAssembler* masm) {
+ // Handle the slow case by jumping to the JavaScript builtin.
+ __ pop(rcx); // pop return address
+ __ push(rax);
+ __ push(rcx); // push return address
+ switch (op_) {
+ case Token::SUB:
+ __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_FUNCTION);
+ break;
+ case Token::BIT_NOT:
+ __ InvokeBuiltin(Builtins::BIT_NOT, JUMP_FUNCTION);
+ break;
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+const char* UnaryOpStub::GetName() {
+ if (name_ != NULL) return name_;
+ const int kMaxNameLength = 100;
+ name_ = Isolate::Current()->bootstrapper()->AllocateAutoDeletedArray(
+ kMaxNameLength);
+ if (name_ == NULL) return "OOM";
+ const char* op_name = Token::Name(op_);
+ const char* overwrite_name = NULL; // Make g++ happy.
+ switch (mode_) {
+ case UNARY_NO_OVERWRITE: overwrite_name = "Alloc"; break;
+ case UNARY_OVERWRITE: overwrite_name = "Overwrite"; break;
+ }
+
+ OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
+ "UnaryOpStub_%s_%s_%s",
+ op_name,
+ overwrite_name,
+ UnaryOpIC::GetName(operand_type_));
+ return name_;
+}
+
+
+Handle<Code> GetBinaryOpStub(int key,
+ BinaryOpIC::TypeInfo type_info,
+ BinaryOpIC::TypeInfo result_type_info) {
+ BinaryOpStub stub(key, type_info, result_type_info);
+ return stub.GetCode();
+}
+
+
+void BinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
__ pop(rcx); // Save return address.
__ push(rdx);
__ push(rax);
@@ -346,36 +701,39 @@
// Patch the caller to an appropriate specialized stub and return the
// operation result to the caller of the stub.
__ TailCallExternalReference(
- ExternalReference(IC_Utility(IC::kTypeRecordingBinaryOp_Patch),
+ ExternalReference(IC_Utility(IC::kBinaryOp_Patch),
masm->isolate()),
5,
1);
}
-void TypeRecordingBinaryOpStub::Generate(MacroAssembler* masm) {
+void BinaryOpStub::Generate(MacroAssembler* masm) {
switch (operands_type_) {
- case TRBinaryOpIC::UNINITIALIZED:
+ case BinaryOpIC::UNINITIALIZED:
GenerateTypeTransition(masm);
break;
- case TRBinaryOpIC::SMI:
+ case BinaryOpIC::SMI:
GenerateSmiStub(masm);
break;
- case TRBinaryOpIC::INT32:
+ case BinaryOpIC::INT32:
UNREACHABLE();
// The int32 case is identical to the Smi case. We avoid creating this
// ic state on x64.
break;
- case TRBinaryOpIC::HEAP_NUMBER:
+ case BinaryOpIC::HEAP_NUMBER:
GenerateHeapNumberStub(masm);
break;
- case TRBinaryOpIC::ODDBALL:
+ case BinaryOpIC::ODDBALL:
GenerateOddballStub(masm);
break;
- case TRBinaryOpIC::STRING:
+ case BinaryOpIC::BOTH_STRING:
+ GenerateBothStringStub(masm);
+ break;
+ case BinaryOpIC::STRING:
GenerateStringStub(masm);
break;
- case TRBinaryOpIC::GENERIC:
+ case BinaryOpIC::GENERIC:
GenerateGeneric(masm);
break;
default:
@@ -384,7 +742,7 @@
}
-const char* TypeRecordingBinaryOpStub::GetName() {
+const char* BinaryOpStub::GetName() {
if (name_ != NULL) return name_;
const int kMaxNameLength = 100;
name_ = Isolate::Current()->bootstrapper()->AllocateAutoDeletedArray(
@@ -400,19 +758,20 @@
}
OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
- "TypeRecordingBinaryOpStub_%s_%s_%s",
+ "BinaryOpStub_%s_%s_%s",
op_name,
overwrite_name,
- TRBinaryOpIC::GetName(operands_type_));
+ BinaryOpIC::GetName(operands_type_));
return name_;
}
-void TypeRecordingBinaryOpStub::GenerateSmiCode(MacroAssembler* masm,
+void BinaryOpStub::GenerateSmiCode(
+ MacroAssembler* masm,
Label* slow,
SmiCodeGenerateHeapNumberResults allow_heapnumber_results) {
- // Arguments to TypeRecordingBinaryOpStub are in rdx and rax.
+ // Arguments to BinaryOpStub are in rdx and rax.
Register left = rdx;
Register right = rax;
@@ -558,10 +917,9 @@
}
-void TypeRecordingBinaryOpStub::GenerateFloatingPointCode(
- MacroAssembler* masm,
- Label* allocation_failure,
- Label* non_numeric_failure) {
+void BinaryOpStub::GenerateFloatingPointCode(MacroAssembler* masm,
+ Label* allocation_failure,
+ Label* non_numeric_failure) {
switch (op_) {
case Token::ADD:
case Token::SUB:
@@ -660,32 +1018,32 @@
// No fall-through from this generated code.
if (FLAG_debug_code) {
__ Abort("Unexpected fall-through in "
- "TypeRecordingBinaryStub::GenerateFloatingPointCode.");
+ "BinaryStub::GenerateFloatingPointCode.");
}
}
-void TypeRecordingBinaryOpStub::GenerateStringAddCode(MacroAssembler* masm) {
+void BinaryOpStub::GenerateStringAddCode(MacroAssembler* masm) {
ASSERT(op_ == Token::ADD);
- NearLabel left_not_string, call_runtime;
+ Label left_not_string, call_runtime;
// Registers containing left and right operands respectively.
Register left = rdx;
Register right = rax;
// Test if left operand is a string.
- __ JumpIfSmi(left, &left_not_string);
+ __ JumpIfSmi(left, &left_not_string, Label::kNear);
__ CmpObjectType(left, FIRST_NONSTRING_TYPE, rcx);
- __ j(above_equal, &left_not_string);
+ __ j(above_equal, &left_not_string, Label::kNear);
StringAddStub string_add_left_stub(NO_STRING_CHECK_LEFT_IN_STUB);
GenerateRegisterArgsPush(masm);
__ TailCallStub(&string_add_left_stub);
// Left operand is not a string, test right.
__ bind(&left_not_string);
- __ JumpIfSmi(right, &call_runtime);
+ __ JumpIfSmi(right, &call_runtime, Label::kNear);
__ CmpObjectType(right, FIRST_NONSTRING_TYPE, rcx);
- __ j(above_equal, &call_runtime);
+ __ j(above_equal, &call_runtime, Label::kNear);
StringAddStub string_add_right_stub(NO_STRING_CHECK_RIGHT_IN_STUB);
GenerateRegisterArgsPush(masm);
@@ -696,7 +1054,7 @@
}
-void TypeRecordingBinaryOpStub::GenerateCallRuntimeCode(MacroAssembler* masm) {
+void BinaryOpStub::GenerateCallRuntimeCode(MacroAssembler* masm) {
GenerateRegisterArgsPush(masm);
switch (op_) {
case Token::ADD:
@@ -738,10 +1096,10 @@
}
-void TypeRecordingBinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
+void BinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
Label call_runtime;
- if (result_type_ == TRBinaryOpIC::UNINITIALIZED ||
- result_type_ == TRBinaryOpIC::SMI) {
+ if (result_type_ == BinaryOpIC::UNINITIALIZED ||
+ result_type_ == BinaryOpIC::SMI) {
// Only allow smi results.
GenerateSmiCode(masm, NULL, NO_HEAPNUMBER_RESULTS);
} else {
@@ -761,17 +1119,47 @@
}
-void TypeRecordingBinaryOpStub::GenerateStringStub(MacroAssembler* masm) {
- ASSERT(operands_type_ == TRBinaryOpIC::STRING);
+void BinaryOpStub::GenerateStringStub(MacroAssembler* masm) {
+ ASSERT(operands_type_ == BinaryOpIC::STRING);
ASSERT(op_ == Token::ADD);
GenerateStringAddCode(masm);
// Try to add arguments as strings, otherwise, transition to the generic
- // TRBinaryOpIC type.
+ // BinaryOpIC type.
GenerateTypeTransition(masm);
}
-void TypeRecordingBinaryOpStub::GenerateOddballStub(MacroAssembler* masm) {
+void BinaryOpStub::GenerateBothStringStub(MacroAssembler* masm) {
+ Label call_runtime;
+ ASSERT(operands_type_ == BinaryOpIC::BOTH_STRING);
+ ASSERT(op_ == Token::ADD);
+ // If both arguments are strings, call the string add stub.
+ // Otherwise, do a transition.
+
+ // Registers containing left and right operands respectively.
+ Register left = rdx;
+ Register right = rax;
+
+ // Test if left operand is a string.
+ __ JumpIfSmi(left, &call_runtime);
+ __ CmpObjectType(left, FIRST_NONSTRING_TYPE, rcx);
+ __ j(above_equal, &call_runtime);
+
+ // Test if right operand is a string.
+ __ JumpIfSmi(right, &call_runtime);
+ __ CmpObjectType(right, FIRST_NONSTRING_TYPE, rcx);
+ __ j(above_equal, &call_runtime);
+
+ StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
+ GenerateRegisterArgsPush(masm);
+ __ TailCallStub(&string_add_stub);
+
+ __ bind(&call_runtime);
+ GenerateTypeTransition(masm);
+}
+
+
+void BinaryOpStub::GenerateOddballStub(MacroAssembler* masm) {
Label call_runtime;
if (op_ == Token::ADD) {
@@ -781,18 +1169,18 @@
}
// Convert oddball arguments to numbers.
- NearLabel check, done;
+ Label check, done;
__ CompareRoot(rdx, Heap::kUndefinedValueRootIndex);
- __ j(not_equal, &check);
+ __ j(not_equal, &check, Label::kNear);
if (Token::IsBitOp(op_)) {
__ xor_(rdx, rdx);
} else {
__ LoadRoot(rdx, Heap::kNanValueRootIndex);
}
- __ jmp(&done);
+ __ jmp(&done, Label::kNear);
__ bind(&check);
__ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
- __ j(not_equal, &done);
+ __ j(not_equal, &done, Label::kNear);
if (Token::IsBitOp(op_)) {
__ xor_(rax, rax);
} else {
@@ -804,7 +1192,7 @@
}
-void TypeRecordingBinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
+void BinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
Label gc_required, not_number;
GenerateFloatingPointCode(masm, &gc_required, ¬_number);
@@ -816,7 +1204,7 @@
}
-void TypeRecordingBinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
+void BinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
Label call_runtime, call_string_add_or_runtime;
GenerateSmiCode(masm, &call_runtime, ALLOW_HEAPNUMBER_RESULTS);
@@ -833,9 +1221,8 @@
}
-void TypeRecordingBinaryOpStub::GenerateHeapResultAllocation(
- MacroAssembler* masm,
- Label* alloc_failure) {
+void BinaryOpStub::GenerateHeapResultAllocation(MacroAssembler* masm,
+ Label* alloc_failure) {
Label skip_allocation;
OverwriteMode mode = mode_;
switch (mode) {
@@ -873,7 +1260,7 @@
}
-void TypeRecordingBinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
+void BinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
__ pop(rcx);
__ push(rdx);
__ push(rax);
@@ -900,11 +1287,10 @@
Label skip_cache;
const bool tagged = (argument_type_ == TAGGED);
if (tagged) {
- NearLabel input_not_smi;
- NearLabel loaded;
+ Label input_not_smi, loaded;
// Test that rax is a number.
__ movq(rax, Operand(rsp, kPointerSize));
- __ JumpIfNotSmi(rax, &input_not_smi);
+ __ JumpIfNotSmi(rax, &input_not_smi, Label::kNear);
// Input is a smi. Untag and load it onto the FPU stack.
// Then load the bits of the double into rbx.
__ SmiToInteger32(rax, rax);
@@ -915,7 +1301,7 @@
__ movq(rdx, xmm1);
__ fld_d(Operand(rsp, 0));
__ addq(rsp, Immediate(kDoubleSize));
- __ jmp(&loaded);
+ __ jmp(&loaded, Label::kNear);
__ bind(&input_not_smi);
// Check if input is a HeapNumber.
@@ -990,9 +1376,9 @@
__ addl(rcx, rcx);
__ lea(rcx, Operand(rax, rcx, times_8, 0));
// Check if cache matches: Double value is stored in uint32_t[2] array.
- NearLabel cache_miss;
+ Label cache_miss;
__ cmpq(rbx, Operand(rcx, 0));
- __ j(not_equal, &cache_miss);
+ __ j(not_equal, &cache_miss, Label::kNear);
// Cache hit!
__ movq(rax, Operand(rcx, 2 * kIntSize));
if (tagged) {
@@ -1100,8 +1486,8 @@
__ j(below, &in_range);
// Check for infinity and NaN. Both return NaN for sin.
__ cmpl(rdi, Immediate(0x7ff));
- NearLabel non_nan_result;
- __ j(not_equal, &non_nan_result);
+ Label non_nan_result;
+ __ j(not_equal, &non_nan_result, Label::kNear);
// Input is +/-Infinity or NaN. Result is NaN.
__ fstp(0);
__ LoadRoot(kScratchRegister, Heap::kNanValueRootIndex);
@@ -1129,7 +1515,7 @@
// Compute st(0) % st(1)
{
- NearLabel partial_remainder_loop;
+ Label partial_remainder_loop;
__ bind(&partial_remainder_loop);
__ fprem1();
__ fwait();
@@ -1166,74 +1552,6 @@
}
-// Get the integer part of a heap number.
-// Overwrites the contents of rdi, rbx and rcx. Result cannot be rdi or rbx.
-void IntegerConvert(MacroAssembler* masm,
- Register result,
- Register source) {
- // Result may be rcx. If result and source are the same register, source will
- // be overwritten.
- ASSERT(!result.is(rdi) && !result.is(rbx));
- // TODO(lrn): When type info reaches here, if value is a 32-bit integer, use
- // cvttsd2si (32-bit version) directly.
- Register double_exponent = rbx;
- Register double_value = rdi;
- NearLabel done, exponent_63_plus;
- // Get double and extract exponent.
- __ movq(double_value, FieldOperand(source, HeapNumber::kValueOffset));
- // Clear result preemptively, in case we need to return zero.
- __ xorl(result, result);
- __ movq(xmm0, double_value); // Save copy in xmm0 in case we need it there.
- // Double to remove sign bit, shift exponent down to least significant bits.
- // and subtract bias to get the unshifted, unbiased exponent.
- __ lea(double_exponent, Operand(double_value, double_value, times_1, 0));
- __ shr(double_exponent, Immediate(64 - HeapNumber::kExponentBits));
- __ subl(double_exponent, Immediate(HeapNumber::kExponentBias));
- // Check whether the exponent is too big for a 63 bit unsigned integer.
- __ cmpl(double_exponent, Immediate(63));
- __ j(above_equal, &exponent_63_plus);
- // Handle exponent range 0..62.
- __ cvttsd2siq(result, xmm0);
- __ jmp(&done);
-
- __ bind(&exponent_63_plus);
- // Exponent negative or 63+.
- __ cmpl(double_exponent, Immediate(83));
- // If exponent negative or above 83, number contains no significant bits in
- // the range 0..2^31, so result is zero, and rcx already holds zero.
- __ j(above, &done);
-
- // Exponent in rage 63..83.
- // Mantissa * 2^exponent contains bits in the range 2^0..2^31, namely
- // the least significant exponent-52 bits.
-
- // Negate low bits of mantissa if value is negative.
- __ addq(double_value, double_value); // Move sign bit to carry.
- __ sbbl(result, result); // And convert carry to -1 in result register.
- // if scratch2 is negative, do (scratch2-1)^-1, otherwise (scratch2-0)^0.
- __ addl(double_value, result);
- // Do xor in opposite directions depending on where we want the result
- // (depending on whether result is rcx or not).
-
- if (result.is(rcx)) {
- __ xorl(double_value, result);
- // Left shift mantissa by (exponent - mantissabits - 1) to save the
- // bits that have positional values below 2^32 (the extra -1 comes from the
- // doubling done above to move the sign bit into the carry flag).
- __ leal(rcx, Operand(double_exponent, -HeapNumber::kMantissaBits - 1));
- __ shll_cl(double_value);
- __ movl(result, double_value);
- } else {
- // As the then-branch, but move double-value to result before shifting.
- __ xorl(result, double_value);
- __ leal(rcx, Operand(double_exponent, -HeapNumber::kMantissaBits - 1));
- __ shll_cl(result);
- }
-
- __ bind(&done);
-}
-
-
// Input: rdx, rax are the left and right objects of a bit op.
// Output: rax, rcx are left and right integers for a bit op.
void FloatingPointHelper::LoadNumbersAsIntegers(MacroAssembler* masm) {
@@ -1390,8 +1708,8 @@
__ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
- NearLabel first_smi, check_second;
- __ JumpIfSmi(first, &first_smi);
+ Label first_smi;
+ __ JumpIfSmi(first, &first_smi, Label::kNear);
__ cmpq(FieldOperand(first, HeapObject::kMapOffset), heap_number_map);
__ j(not_equal, on_not_smis);
// Convert HeapNumber to smi if possible.
@@ -1406,7 +1724,6 @@
__ j(not_equal, on_not_smis);
__ Integer32ToSmi(first, smi_result);
- __ bind(&check_second);
__ JumpIfSmi(second, (on_success != NULL) ? on_success : &done);
__ bind(&first_smi);
if (FLAG_debug_code) {
@@ -1432,91 +1749,6 @@
}
-void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
- Label slow, done;
-
- if (op_ == Token::SUB) {
- if (include_smi_code_) {
- // Check whether the value is a smi.
- Label try_float;
- __ JumpIfNotSmi(rax, &try_float);
- if (negative_zero_ == kIgnoreNegativeZero) {
- __ SmiCompare(rax, Smi::FromInt(0));
- __ j(equal, &done);
- }
- __ SmiNeg(rax, rax, &done);
- __ jmp(&slow); // zero, if not handled above, and Smi::kMinValue.
-
- // Try floating point case.
- __ bind(&try_float);
- } else if (FLAG_debug_code) {
- __ AbortIfSmi(rax);
- }
-
- __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
- Heap::kHeapNumberMapRootIndex);
- __ j(not_equal, &slow);
- // Operand is a float, negate its value by flipping sign bit.
- __ movq(rdx, FieldOperand(rax, HeapNumber::kValueOffset));
- __ Set(kScratchRegister, 0x01);
- __ shl(kScratchRegister, Immediate(63));
- __ xor_(rdx, kScratchRegister); // Flip sign.
- // rdx is value to store.
- if (overwrite_ == UNARY_OVERWRITE) {
- __ movq(FieldOperand(rax, HeapNumber::kValueOffset), rdx);
- } else {
- __ AllocateHeapNumber(rcx, rbx, &slow);
- // rcx: allocated 'empty' number
- __ movq(FieldOperand(rcx, HeapNumber::kValueOffset), rdx);
- __ movq(rax, rcx);
- }
- } else if (op_ == Token::BIT_NOT) {
- if (include_smi_code_) {
- Label try_float;
- __ JumpIfNotSmi(rax, &try_float);
- __ SmiNot(rax, rax);
- __ jmp(&done);
- // Try floating point case.
- __ bind(&try_float);
- } else if (FLAG_debug_code) {
- __ AbortIfSmi(rax);
- }
-
- // Check if the operand is a heap number.
- __ CompareRoot(FieldOperand(rax, HeapObject::kMapOffset),
- Heap::kHeapNumberMapRootIndex);
- __ j(not_equal, &slow);
-
- // Convert the heap number in rax to an untagged integer in rcx.
- IntegerConvert(masm, rax, rax);
-
- // Do the bitwise operation and smi tag the result.
- __ notl(rax);
- __ Integer32ToSmi(rax, rax);
- }
-
- // Return from the stub.
- __ bind(&done);
- __ StubReturn(1);
-
- // Handle the slow case by jumping to the JavaScript builtin.
- __ bind(&slow);
- __ pop(rcx); // pop return address
- __ push(rax);
- __ push(rcx); // push return address
- switch (op_) {
- case Token::SUB:
- __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_FUNCTION);
- break;
- case Token::BIT_NOT:
- __ InvokeBuiltin(Builtins::BIT_NOT, JUMP_FUNCTION);
- break;
- default:
- UNREACHABLE();
- }
-}
-
-
void MathPowStub::Generate(MacroAssembler* masm) {
// Registers are used as follows:
// rdx = base
@@ -1562,20 +1794,20 @@
__ movq(rdx, rax);
// Get absolute value of exponent.
- NearLabel no_neg;
+ Label no_neg;
__ cmpl(rax, Immediate(0));
- __ j(greater_equal, &no_neg);
+ __ j(greater_equal, &no_neg, Label::kNear);
__ negl(rax);
__ bind(&no_neg);
// Load xmm1 with 1.
- __ movsd(xmm1, xmm3);
- NearLabel while_true;
- NearLabel no_multiply;
+ __ movaps(xmm1, xmm3);
+ Label while_true;
+ Label no_multiply;
__ bind(&while_true);
__ shrl(rax, Immediate(1));
- __ j(not_carry, &no_multiply);
+ __ j(not_carry, &no_multiply, Label::kNear);
__ mulsd(xmm1, xmm0);
__ bind(&no_multiply);
__ mulsd(xmm0, xmm0);
@@ -1587,8 +1819,8 @@
__ j(positive, &allocate_return);
// Special case if xmm1 has reached infinity.
__ divsd(xmm3, xmm1);
- __ movsd(xmm1, xmm3);
- __ xorpd(xmm0, xmm0);
+ __ movaps(xmm1, xmm3);
+ __ xorps(xmm0, xmm0);
__ ucomisd(xmm0, xmm1);
__ j(equal, &call_runtime);
@@ -1605,12 +1837,11 @@
__ ucomisd(xmm1, xmm1);
__ j(parity_even, &call_runtime);
- NearLabel base_not_smi;
- NearLabel handle_special_cases;
- __ JumpIfNotSmi(rdx, &base_not_smi);
+ Label base_not_smi, handle_special_cases;
+ __ JumpIfNotSmi(rdx, &base_not_smi, Label::kNear);
__ SmiToInteger32(rdx, rdx);
__ cvtlsi2sd(xmm0, rdx);
- __ jmp(&handle_special_cases);
+ __ jmp(&handle_special_cases, Label::kNear);
__ bind(&base_not_smi);
__ CompareRoot(FieldOperand(rdx, HeapObject::kMapOffset),
@@ -1625,22 +1856,22 @@
// base is in xmm0 and exponent is in xmm1.
__ bind(&handle_special_cases);
- NearLabel not_minus_half;
+ Label not_minus_half;
// Test for -0.5.
// Load xmm2 with -0.5.
__ movq(rcx, V8_UINT64_C(0xBFE0000000000000), RelocInfo::NONE);
__ movq(xmm2, rcx);
// xmm2 now has -0.5.
__ ucomisd(xmm2, xmm1);
- __ j(not_equal, ¬_minus_half);
+ __ j(not_equal, ¬_minus_half, Label::kNear);
// Calculates reciprocal of square root.
// sqrtsd returns -0 when input is -0. ECMA spec requires +0.
- __ xorpd(xmm1, xmm1);
+ __ xorps(xmm1, xmm1);
__ addsd(xmm1, xmm0);
__ sqrtsd(xmm1, xmm1);
__ divsd(xmm3, xmm1);
- __ movsd(xmm1, xmm3);
+ __ movaps(xmm1, xmm3);
__ jmp(&allocate_return);
// Test for 0.5.
@@ -1653,8 +1884,8 @@
__ j(not_equal, &call_runtime);
// Calculates square root.
// sqrtsd returns -0 when input is -0. ECMA spec requires +0.
- __ xorpd(xmm1, xmm1);
- __ addsd(xmm1, xmm0);
+ __ xorps(xmm1, xmm1);
+ __ addsd(xmm1, xmm0); // Convert -0 to 0.
__ sqrtsd(xmm1, xmm1);
__ bind(&allocate_return);
@@ -1951,7 +2182,7 @@
// rax: RegExp data (FixedArray)
// Check the representation and encoding of the subject string.
- NearLabel seq_ascii_string, seq_two_byte_string, check_code;
+ Label seq_ascii_string, seq_two_byte_string, check_code;
__ movq(rdi, Operand(rsp, kSubjectOffset));
__ movq(rbx, FieldOperand(rdi, HeapObject::kMapOffset));
__ movzxbl(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset));
@@ -1959,10 +2190,10 @@
__ andb(rbx, Immediate(
kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask));
STATIC_ASSERT((kStringTag | kSeqStringTag | kTwoByteStringTag) == 0);
- __ j(zero, &seq_two_byte_string);
+ __ j(zero, &seq_two_byte_string, Label::kNear);
// Any other flat string must be a flat ascii string.
__ testb(rbx, Immediate(kIsNotStringMask | kStringRepresentationMask));
- __ j(zero, &seq_ascii_string);
+ __ j(zero, &seq_ascii_string, Label::kNear);
// Check for flat cons string.
// A flat cons string is a cons string where the second part is the empty
@@ -1986,7 +2217,7 @@
__ testb(FieldOperand(rbx, Map::kInstanceTypeOffset),
Immediate(kStringRepresentationMask | kStringEncodingMask));
STATIC_ASSERT((kSeqStringTag | kTwoByteStringTag) == 0);
- __ j(zero, &seq_two_byte_string);
+ __ j(zero, &seq_two_byte_string, Label::kNear);
// Any other flat string must be ascii.
__ testb(FieldOperand(rbx, Map::kInstanceTypeOffset),
Immediate(kStringRepresentationMask));
@@ -1997,7 +2228,7 @@
// rax: RegExp data (FixedArray)
__ movq(r11, FieldOperand(rax, JSRegExp::kDataAsciiCodeOffset));
__ Set(rcx, 1); // Type is ascii.
- __ jmp(&check_code);
+ __ jmp(&check_code, Label::kNear);
__ bind(&seq_two_byte_string);
// rdi: subject string (flat two-byte)
@@ -2008,9 +2239,8 @@
__ bind(&check_code);
// Check that the irregexp code has been generated for the actual string
// encoding. If it has, the field contains a code object otherwise it contains
- // the hole.
- __ CmpObjectType(r11, CODE_TYPE, kScratchRegister);
- __ j(not_equal, &runtime);
+ // smi (code flushing support)
+ __ JumpIfSmi(r11, &runtime);
// rdi: subject string
// rcx: encoding of subject string (1 if ascii, 0 if two_byte);
@@ -2083,13 +2313,13 @@
// Argument 4: End of string data
// Argument 3: Start of string data
- NearLabel setup_two_byte, setup_rest;
+ Label setup_two_byte, setup_rest;
__ testb(rcx, rcx); // Last use of rcx as encoding of subject string.
- __ j(zero, &setup_two_byte);
+ __ j(zero, &setup_two_byte, Label::kNear);
__ SmiToInteger32(rcx, FieldOperand(rdi, String::kLengthOffset));
__ lea(arg4, FieldOperand(rdi, rcx, times_1, SeqAsciiString::kHeaderSize));
__ lea(arg3, FieldOperand(rdi, rbx, times_1, SeqAsciiString::kHeaderSize));
- __ jmp(&setup_rest);
+ __ jmp(&setup_rest, Label::kNear);
__ bind(&setup_two_byte);
__ SmiToInteger32(rcx, FieldOperand(rdi, String::kLengthOffset));
__ lea(arg4, FieldOperand(rdi, rcx, times_2, SeqTwoByteString::kHeaderSize));
@@ -2114,10 +2344,10 @@
__ LeaveApiExitFrame();
// Check the result.
- NearLabel success;
+ Label success;
Label exception;
__ cmpl(rax, Immediate(NativeRegExpMacroAssembler::SUCCESS));
- __ j(equal, &success);
+ __ j(equal, &success, Label::kNear);
__ cmpl(rax, Immediate(NativeRegExpMacroAssembler::EXCEPTION));
__ j(equal, &exception);
__ cmpl(rax, Immediate(NativeRegExpMacroAssembler::FAILURE));
@@ -2166,12 +2396,12 @@
// rbx: last_match_info backing store (FixedArray)
// rcx: offsets vector
// rdx: number of capture registers
- NearLabel next_capture, done;
+ Label next_capture, done;
// Capture register counter starts from number of capture registers and
// counts down until wraping after zero.
__ bind(&next_capture);
__ subq(rdx, Immediate(1));
- __ j(negative, &done);
+ __ j(negative, &done, Label::kNear);
// Read the value from the static offsets vector buffer and make it a smi.
__ movl(rdi, Operand(rcx, rdx, times_int_size, 0));
__ Integer32ToSmi(rdi, rdi);
@@ -2204,8 +2434,8 @@
__ movq(pending_exception_operand, rdx);
__ CompareRoot(rax, Heap::kTerminationExceptionRootIndex);
- NearLabel termination_exception;
- __ j(equal, &termination_exception);
+ Label termination_exception;
+ __ j(equal, &termination_exception, Label::kNear);
__ Throw(rax);
__ bind(&termination_exception);
@@ -2330,9 +2560,13 @@
// Heap::GetNumberStringCache.
Label is_smi;
Label load_result_from_cache;
+ Factory* factory = masm->isolate()->factory();
if (!object_is_smi) {
__ JumpIfSmi(object, &is_smi);
- __ CheckMap(object, FACTORY->heap_number_map(), not_found, true);
+ __ CheckMap(object,
+ factory->heap_number_map(),
+ not_found,
+ DONT_DO_SMI_CHECK);
STATIC_ASSERT(8 == kDoubleSize);
__ movl(scratch, FieldOperand(object, HeapNumber::kValueOffset + 4));
@@ -2419,6 +2653,7 @@
ASSERT(lhs_.is(no_reg) && rhs_.is(no_reg));
Label check_unequal_objects, done;
+ Factory* factory = masm->isolate()->factory();
// Compare two smis if required.
if (include_smi_compare_) {
@@ -2446,16 +2681,16 @@
// Two identical objects are equal unless they are both NaN or undefined.
{
- NearLabel not_identical;
+ Label not_identical;
__ cmpq(rax, rdx);
- __ j(not_equal, ¬_identical);
+ __ j(not_equal, ¬_identical, Label::kNear);
if (cc_ != equal) {
// Check for undefined. undefined OP undefined is false even though
// undefined == undefined.
- NearLabel check_for_nan;
+ Label check_for_nan;
__ CompareRoot(rdx, Heap::kUndefinedValueRootIndex);
- __ j(not_equal, &check_for_nan);
+ __ j(not_equal, &check_for_nan, Label::kNear);
__ Set(rax, NegativeComparisonResult(cc_));
__ ret(0);
__ bind(&check_for_nan);
@@ -2466,20 +2701,19 @@
// Note: if cc_ != equal, never_nan_nan_ is not used.
// We cannot set rax to EQUAL until just before return because
// rax must be unchanged on jump to not_identical.
-
if (never_nan_nan_ && (cc_ == equal)) {
__ Set(rax, EQUAL);
__ ret(0);
} else {
- NearLabel heap_number;
+ Label heap_number;
// If it's not a heap number, then return equal for (in)equality operator.
__ Cmp(FieldOperand(rdx, HeapObject::kMapOffset),
- FACTORY->heap_number_map());
- __ j(equal, &heap_number);
+ factory->heap_number_map());
+ __ j(equal, &heap_number, Label::kNear);
if (cc_ != equal) {
// Call runtime on identical JSObjects. Otherwise return equal.
__ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rcx);
- __ j(above_equal, ¬_identical);
+ __ j(above_equal, ¬_identical, Label::kNear);
}
__ Set(rax, EQUAL);
__ ret(0);
@@ -2519,7 +2753,7 @@
// Check if the non-smi operand is a heap number.
__ Cmp(FieldOperand(rbx, HeapObject::kMapOffset),
- FACTORY->heap_number_map());
+ factory->heap_number_map());
// If heap number, handle it in the slow case.
__ j(equal, &slow);
// Return non-equal. ebx (the lower half of rbx) is not zero.
@@ -2535,9 +2769,9 @@
// If the first object is a JS object, we have done pointer comparison.
STATIC_ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
- NearLabel first_non_object;
+ Label first_non_object;
__ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rcx);
- __ j(below, &first_non_object);
+ __ j(below, &first_non_object, Label::kNear);
// Return non-zero (eax (not rax) is not zero)
Label return_not_equal;
STATIC_ASSERT(kHeapObjectTag != 0);
@@ -2564,14 +2798,14 @@
// Generate the number comparison code.
if (include_number_compare_) {
Label non_number_comparison;
- NearLabel unordered;
+ Label unordered;
FloatingPointHelper::LoadSSE2UnknownOperands(masm, &non_number_comparison);
__ xorl(rax, rax);
__ xorl(rcx, rcx);
__ ucomisd(xmm0, xmm1);
// Don't base result on EFLAGS when a NaN is involved.
- __ j(parity_even, &unordered);
+ __ j(parity_even, &unordered, Label::kNear);
// Return a result of -1, 0, or 1, based on EFLAGS.
__ setcc(above, rax);
__ setcc(below, rcx);
@@ -2611,13 +2845,21 @@
rdx, rax, rcx, rbx, &check_unequal_objects);
// Inline comparison of ascii strings.
- StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
+ if (cc_ == equal) {
+ StringCompareStub::GenerateFlatAsciiStringEquals(masm,
rdx,
rax,
rcx,
- rbx,
- rdi,
- r8);
+ rbx);
+ } else {
+ StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
+ rdx,
+ rax,
+ rcx,
+ rbx,
+ rdi,
+ r8);
+ }
#ifdef DEBUG
__ Abort("Unexpected fall-through from string comparison");
@@ -2628,7 +2870,7 @@
// Not strict equality. Objects are unequal if
// they are both JSObjects and not undetectable,
// and their pointers are different.
- NearLabel not_both_objects, return_unequal;
+ Label not_both_objects, return_unequal;
// At most one is a smi, so we can test for smi by adding the two.
// A smi plus a heap object has the low bit set, a heap object plus
// a heap object has the low bit clear.
@@ -2636,17 +2878,17 @@
STATIC_ASSERT(kSmiTagMask == 1);
__ lea(rcx, Operand(rax, rdx, times_1, 0));
__ testb(rcx, Immediate(kSmiTagMask));
- __ j(not_zero, ¬_both_objects);
+ __ j(not_zero, ¬_both_objects, Label::kNear);
__ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rbx);
- __ j(below, ¬_both_objects);
+ __ j(below, ¬_both_objects, Label::kNear);
__ CmpObjectType(rdx, FIRST_JS_OBJECT_TYPE, rcx);
- __ j(below, ¬_both_objects);
+ __ j(below, ¬_both_objects, Label::kNear);
__ testb(FieldOperand(rbx, Map::kBitFieldOffset),
Immediate(1 << Map::kIsUndetectable));
- __ j(zero, &return_unequal);
+ __ j(zero, &return_unequal, Label::kNear);
__ testb(FieldOperand(rcx, Map::kBitFieldOffset),
Immediate(1 << Map::kIsUndetectable));
- __ j(zero, &return_unequal);
+ __ j(zero, &return_unequal, Label::kNear);
// The objects are both undetectable, so they both compare as the value
// undefined, and are equal.
__ Set(rax, EQUAL);
@@ -2704,30 +2946,22 @@
void CallFunctionStub::Generate(MacroAssembler* masm) {
Label slow;
- // If the receiver might be a value (string, number or boolean) check for this
- // and box it if it is.
- if (ReceiverMightBeValue()) {
+ // The receiver might implicitly be the global object. This is
+ // indicated by passing the hole as the receiver to the call
+ // function stub.
+ if (ReceiverMightBeImplicit()) {
+ Label call;
// Get the receiver from the stack.
// +1 ~ return address
- Label receiver_is_value, receiver_is_js_object;
__ movq(rax, Operand(rsp, (argc_ + 1) * kPointerSize));
-
- // Check if receiver is a smi (which is a number value).
- __ JumpIfSmi(rax, &receiver_is_value);
-
- // Check if the receiver is a valid JS object.
- __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rdi);
- __ j(above_equal, &receiver_is_js_object);
-
- // Call the runtime to box the value.
- __ bind(&receiver_is_value);
- __ EnterInternalFrame();
- __ push(rax);
- __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
- __ LeaveInternalFrame();
- __ movq(Operand(rsp, (argc_ + 1) * kPointerSize), rax);
-
- __ bind(&receiver_is_js_object);
+ // Call as function is indicated with the hole.
+ __ CompareRoot(rax, Heap::kTheHoleValueRootIndex);
+ __ j(not_equal, &call, Label::kNear);
+ // Patch the receiver on the stack with the global receiver object.
+ __ movq(rbx, GlobalObjectOperand());
+ __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset));
+ __ movq(Operand(rsp, (argc_ + 1) * kPointerSize), rbx);
+ __ bind(&call);
}
// Get the function to call from the stack.
@@ -2742,7 +2976,23 @@
// Fast-case: Just invoke the function.
ParameterCount actual(argc_);
- __ InvokeFunction(rdi, actual, JUMP_FUNCTION);
+
+ if (ReceiverMightBeImplicit()) {
+ Label call_as_function;
+ __ CompareRoot(rax, Heap::kTheHoleValueRootIndex);
+ __ j(equal, &call_as_function);
+ __ InvokeFunction(rdi,
+ actual,
+ JUMP_FUNCTION,
+ NullCallWrapper(),
+ CALL_AS_METHOD);
+ __ bind(&call_as_function);
+ }
+ __ InvokeFunction(rdi,
+ actual,
+ JUMP_FUNCTION,
+ NullCallWrapper(),
+ CALL_AS_FUNCTION);
// Slow-case: Non-function called.
__ bind(&slow);
@@ -2876,11 +3126,11 @@
// Handling of failure.
__ bind(&failure_returned);
- NearLabel retry;
+ Label retry;
// If the returned exception is RETRY_AFTER_GC continue at retry label
STATIC_ASSERT(Failure::RETRY_AFTER_GC == 0);
__ testl(rax, Immediate(((1 << kFailureTypeTagSize) - 1) << kFailureTagSize));
- __ j(zero, &retry);
+ __ j(zero, &retry, Label::kNear);
// Special handling of out of memory exceptions.
__ movq(kScratchRegister, Failure::OutOfMemoryException(), RelocInfo::NONE);
@@ -3180,11 +3430,11 @@
// real lookup and update the call site cache.
if (!HasCallSiteInlineCheck()) {
// Look up the function and the map in the instanceof cache.
- NearLabel miss;
+ Label miss;
__ CompareRoot(rdx, Heap::kInstanceofCacheFunctionRootIndex);
- __ j(not_equal, &miss);
+ __ j(not_equal, &miss, Label::kNear);
__ CompareRoot(rax, Heap::kInstanceofCacheMapRootIndex);
- __ j(not_equal, &miss);
+ __ j(not_equal, &miss, Label::kNear);
__ LoadRoot(rax, Heap::kInstanceofCacheAnswerRootIndex);
__ ret(2 * kPointerSize);
__ bind(&miss);
@@ -3220,15 +3470,15 @@
__ movq(rcx, FieldOperand(rax, Map::kPrototypeOffset));
// Loop through the prototype chain looking for the function prototype.
- NearLabel loop, is_instance, is_not_instance;
+ Label loop, is_instance, is_not_instance;
__ LoadRoot(kScratchRegister, Heap::kNullValueRootIndex);
__ bind(&loop);
__ cmpq(rcx, rbx);
- __ j(equal, &is_instance);
+ __ j(equal, &is_instance, Label::kNear);
__ cmpq(rcx, kScratchRegister);
// The code at is_not_instance assumes that kScratchRegister contains a
// non-zero GCable value (the null object in this case).
- __ j(equal, &is_not_instance);
+ __ j(equal, &is_not_instance, Label::kNear);
__ movq(rcx, FieldOperand(rcx, HeapObject::kMapOffset));
__ movq(rcx, FieldOperand(rcx, Map::kPrototypeOffset));
__ jmp(&loop);
@@ -3449,10 +3699,14 @@
MacroAssembler* masm, const RuntimeCallHelper& call_helper) {
__ Abort("Unexpected fallthrough to CharCodeAt slow case");
+ Factory* factory = masm->isolate()->factory();
// Index is not a smi.
__ bind(&index_not_smi_);
// If index is a heap number, try converting it to an integer.
- __ CheckMap(index_, FACTORY->heap_number_map(), index_not_number_, true);
+ __ CheckMap(index_,
+ factory->heap_number_map(),
+ index_not_number_,
+ DONT_DO_SMI_CHECK);
call_helper.BeforeCall(masm);
__ push(object_);
__ push(index_);
@@ -3592,10 +3846,10 @@
// rax: first string
// rdx: second string
// Check if either of the strings are empty. In that case return the other.
- NearLabel second_not_zero_length, both_not_zero_length;
+ Label second_not_zero_length, both_not_zero_length;
__ movq(rcx, FieldOperand(rdx, String::kLengthOffset));
__ SmiTest(rcx);
- __ j(not_zero, &second_not_zero_length);
+ __ j(not_zero, &second_not_zero_length, Label::kNear);
// Second string is empty, result is first string which is already in rax.
Counters* counters = masm->isolate()->counters();
__ IncrementCounter(counters->string_add_native(), 1);
@@ -3603,7 +3857,7 @@
__ bind(&second_not_zero_length);
__ movq(rbx, FieldOperand(rax, String::kLengthOffset));
__ SmiTest(rbx);
- __ j(not_zero, &both_not_zero_length);
+ __ j(not_zero, &both_not_zero_length, Label::kNear);
// First string is empty, result is second string which is in rdx.
__ movq(rax, rdx);
__ IncrementCounter(counters->string_add_native(), 1);
@@ -3897,9 +4151,9 @@
ASSERT(count.is(rcx)); // rep movs count
// Nothing to do for zero characters.
- NearLabel done;
+ Label done;
__ testl(count, count);
- __ j(zero, &done);
+ __ j(zero, &done, Label::kNear);
// Make count the number of bytes to copy.
if (!ascii) {
@@ -3908,9 +4162,9 @@
}
// Don't enter the rep movs if there are less than 4 bytes to copy.
- NearLabel last_bytes;
+ Label last_bytes;
__ testl(count, Immediate(~7));
- __ j(zero, &last_bytes);
+ __ j(zero, &last_bytes, Label::kNear);
// Copy from edi to esi using rep movs instruction.
__ movl(kScratchRegister, count);
@@ -3924,7 +4178,7 @@
// Check if there are more bytes to copy.
__ bind(&last_bytes);
__ testl(count, count);
- __ j(zero, &done);
+ __ j(zero, &done, Label::kNear);
// Copy remaining characters.
Label loop;
@@ -3952,10 +4206,10 @@
// Make sure that both characters are not digits as such strings has a
// different hash algorithm. Don't try to look for these in the symbol table.
- NearLabel not_array_index;
+ Label not_array_index;
__ leal(scratch, Operand(c1, -'0'));
__ cmpl(scratch, Immediate(static_cast<int>('9' - '0')));
- __ j(above, ¬_array_index);
+ __ j(above, ¬_array_index, Label::kNear);
__ leal(scratch, Operand(c2, -'0'));
__ cmpl(scratch, Immediate(static_cast<int>('9' - '0')));
__ j(below_equal, not_found);
@@ -4017,9 +4271,9 @@
SymbolTable::kElementsStartOffset));
// If entry is undefined no string with this hash can be found.
- NearLabel is_string;
+ Label is_string;
__ CmpObjectType(candidate, ODDBALL_TYPE, map);
- __ j(not_equal, &is_string);
+ __ j(not_equal, &is_string, Label::kNear);
__ CompareRoot(candidate, Heap::kUndefinedValueRootIndex);
__ j(equal, not_found);
@@ -4263,6 +4517,47 @@
}
+void StringCompareStub::GenerateFlatAsciiStringEquals(MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register scratch1,
+ Register scratch2) {
+ Register length = scratch1;
+
+ // Compare lengths.
+ Label check_zero_length;
+ __ movq(length, FieldOperand(left, String::kLengthOffset));
+ __ SmiCompare(length, FieldOperand(right, String::kLengthOffset));
+ __ j(equal, &check_zero_length, Label::kNear);
+ __ Move(rax, Smi::FromInt(NOT_EQUAL));
+ __ ret(0);
+
+ // Check if the length is zero.
+ Label compare_chars;
+ __ bind(&check_zero_length);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ SmiTest(length);
+ __ j(not_zero, &compare_chars, Label::kNear);
+ __ Move(rax, Smi::FromInt(EQUAL));
+ __ ret(0);
+
+ // Compare characters.
+ __ bind(&compare_chars);
+ Label strings_not_equal;
+ GenerateAsciiCharsCompareLoop(masm, left, right, length, scratch2,
+ &strings_not_equal, Label::kNear);
+
+ // Characters are equal.
+ __ Move(rax, Smi::FromInt(EQUAL));
+ __ ret(0);
+
+ // Characters are not equal.
+ __ bind(&strings_not_equal);
+ __ Move(rax, Smi::FromInt(NOT_EQUAL));
+ __ ret(0);
+}
+
+
void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
Register left,
Register right,
@@ -4282,8 +4577,8 @@
FieldOperand(right, String::kLengthOffset));
// Register scratch4 now holds left.length - right.length.
const Register length_difference = scratch4;
- NearLabel left_shorter;
- __ j(less, &left_shorter);
+ Label left_shorter;
+ __ j(less, &left_shorter, Label::kNear);
// The right string isn't longer that the left one.
// Get the right string's length by subtracting the (non-negative) difference
// from the left string's length.
@@ -4292,54 +4587,30 @@
// Register scratch1 now holds Min(left.length, right.length).
const Register min_length = scratch1;
- NearLabel compare_lengths;
+ Label compare_lengths;
// If min-length is zero, go directly to comparing lengths.
__ SmiTest(min_length);
- __ j(zero, &compare_lengths);
+ __ j(zero, &compare_lengths, Label::kNear);
- __ SmiToInteger32(min_length, min_length);
+ // Compare loop.
+ Label result_not_equal;
+ GenerateAsciiCharsCompareLoop(masm, left, right, min_length, scratch2,
+ &result_not_equal, Label::kNear);
- // Registers scratch2 and scratch3 are free.
- NearLabel result_not_equal;
- Label loop;
- {
- // Check characters 0 .. min_length - 1 in a loop.
- // Use scratch3 as loop index, min_length as limit and scratch2
- // for computation.
- const Register index = scratch3;
- __ Set(index, 0); // Index into strings.
- __ bind(&loop);
- // Compare characters.
- // TODO(lrn): Could we load more than one character at a time?
- __ movb(scratch2, FieldOperand(left,
- index,
- times_1,
- SeqAsciiString::kHeaderSize));
- // Increment index and use -1 modifier on next load to give
- // the previous load extra time to complete.
- __ addl(index, Immediate(1));
- __ cmpb(scratch2, FieldOperand(right,
- index,
- times_1,
- SeqAsciiString::kHeaderSize - 1));
- __ j(not_equal, &result_not_equal);
- __ cmpl(index, min_length);
- __ j(not_equal, &loop);
- }
// Completed loop without finding different characters.
// Compare lengths (precomputed).
__ bind(&compare_lengths);
__ SmiTest(length_difference);
- __ j(not_zero, &result_not_equal);
+ __ j(not_zero, &result_not_equal, Label::kNear);
// Result is EQUAL.
__ Move(rax, Smi::FromInt(EQUAL));
__ ret(0);
- NearLabel result_greater;
+ Label result_greater;
__ bind(&result_not_equal);
// Unequal comparison of left to right, either character or length.
- __ j(greater, &result_greater);
+ __ j(greater, &result_greater, Label::kNear);
// Result is LESS.
__ Move(rax, Smi::FromInt(LESS));
@@ -4352,6 +4623,36 @@
}
+void StringCompareStub::GenerateAsciiCharsCompareLoop(
+ MacroAssembler* masm,
+ Register left,
+ Register right,
+ Register length,
+ Register scratch,
+ Label* chars_not_equal,
+ Label::Distance near_jump) {
+ // Change index to run from -length to -1 by adding length to string
+ // start. This means that loop ends when index reaches zero, which
+ // doesn't need an additional compare.
+ __ SmiToInteger32(length, length);
+ __ lea(left,
+ FieldOperand(left, length, times_1, SeqAsciiString::kHeaderSize));
+ __ lea(right,
+ FieldOperand(right, length, times_1, SeqAsciiString::kHeaderSize));
+ __ neg(length);
+ Register index = length; // index = -length;
+
+ // Compare loop.
+ Label loop;
+ __ bind(&loop);
+ __ movb(scratch, Operand(left, index, times_1, 0));
+ __ cmpb(scratch, Operand(right, index, times_1, 0));
+ __ j(not_equal, chars_not_equal, near_jump);
+ __ addq(index, Immediate(1));
+ __ j(not_zero, &loop);
+}
+
+
void StringCompareStub::Generate(MacroAssembler* masm) {
Label runtime;
@@ -4364,9 +4665,9 @@
__ movq(rax, Operand(rsp, 1 * kPointerSize)); // right
// Check for identity.
- NearLabel not_same;
+ Label not_same;
__ cmpq(rdx, rax);
- __ j(not_equal, ¬_same);
+ __ j(not_equal, ¬_same, Label::kNear);
__ Move(rax, Smi::FromInt(EQUAL));
Counters* counters = masm->isolate()->counters();
__ IncrementCounter(counters->string_compare_native(), 1);
@@ -4394,16 +4695,16 @@
void ICCompareStub::GenerateSmis(MacroAssembler* masm) {
ASSERT(state_ == CompareIC::SMIS);
- NearLabel miss;
- __ JumpIfNotBothSmi(rdx, rax, &miss);
+ Label miss;
+ __ JumpIfNotBothSmi(rdx, rax, &miss, Label::kNear);
if (GetCondition() == equal) {
// For equality we do not care about the sign of the result.
__ subq(rax, rdx);
} else {
- NearLabel done;
+ Label done;
__ subq(rdx, rax);
- __ j(no_overflow, &done);
+ __ j(no_overflow, &done, Label::kNear);
// Correct sign of result in case of overflow.
__ SmiNot(rdx, rdx);
__ bind(&done);
@@ -4419,16 +4720,16 @@
void ICCompareStub::GenerateHeapNumbers(MacroAssembler* masm) {
ASSERT(state_ == CompareIC::HEAP_NUMBERS);
- NearLabel generic_stub;
- NearLabel unordered;
- NearLabel miss;
+ Label generic_stub;
+ Label unordered;
+ Label miss;
Condition either_smi = masm->CheckEitherSmi(rax, rdx);
- __ j(either_smi, &generic_stub);
+ __ j(either_smi, &generic_stub, Label::kNear);
__ CmpObjectType(rax, HEAP_NUMBER_TYPE, rcx);
- __ j(not_equal, &miss);
+ __ j(not_equal, &miss, Label::kNear);
__ CmpObjectType(rdx, HEAP_NUMBER_TYPE, rcx);
- __ j(not_equal, &miss);
+ __ j(not_equal, &miss, Label::kNear);
// Load left and right operand
__ movsd(xmm0, FieldOperand(rdx, HeapNumber::kValueOffset));
@@ -4438,7 +4739,7 @@
__ ucomisd(xmm0, xmm1);
// Don't base result on EFLAGS when a NaN is involved.
- __ j(parity_even, &unordered);
+ __ j(parity_even, &unordered, Label::kNear);
// Return a result of -1, 0, or 1, based on EFLAGS.
// Performing mov, because xor would destroy the flag register.
@@ -4459,16 +4760,133 @@
}
+void ICCompareStub::GenerateSymbols(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::SYMBOLS);
+ ASSERT(GetCondition() == equal);
+
+ // Registers containing left and right operands respectively.
+ Register left = rdx;
+ Register right = rax;
+ Register tmp1 = rcx;
+ Register tmp2 = rbx;
+
+ // Check that both operands are heap objects.
+ Label miss;
+ Condition cond = masm->CheckEitherSmi(left, right, tmp1);
+ __ j(cond, &miss, Label::kNear);
+
+ // Check that both operands are symbols.
+ __ movq(tmp1, FieldOperand(left, HeapObject::kMapOffset));
+ __ movq(tmp2, FieldOperand(right, HeapObject::kMapOffset));
+ __ movzxbq(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
+ __ movzxbq(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
+ STATIC_ASSERT(kSymbolTag != 0);
+ __ and_(tmp1, tmp2);
+ __ testb(tmp1, Immediate(kIsSymbolMask));
+ __ j(zero, &miss, Label::kNear);
+
+ // Symbols are compared by identity.
+ Label done;
+ __ cmpq(left, right);
+ // Make sure rax is non-zero. At this point input operands are
+ // guaranteed to be non-zero.
+ ASSERT(right.is(rax));
+ __ j(not_equal, &done, Label::kNear);
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ Move(rax, Smi::FromInt(EQUAL));
+ __ bind(&done);
+ __ ret(0);
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
+void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
+ ASSERT(state_ == CompareIC::STRINGS);
+ ASSERT(GetCondition() == equal);
+ Label miss;
+
+ // Registers containing left and right operands respectively.
+ Register left = rdx;
+ Register right = rax;
+ Register tmp1 = rcx;
+ Register tmp2 = rbx;
+ Register tmp3 = rdi;
+
+ // Check that both operands are heap objects.
+ Condition cond = masm->CheckEitherSmi(left, right, tmp1);
+ __ j(cond, &miss);
+
+ // Check that both operands are strings. This leaves the instance
+ // types loaded in tmp1 and tmp2.
+ __ movq(tmp1, FieldOperand(left, HeapObject::kMapOffset));
+ __ movq(tmp2, FieldOperand(right, HeapObject::kMapOffset));
+ __ movzxbq(tmp1, FieldOperand(tmp1, Map::kInstanceTypeOffset));
+ __ movzxbq(tmp2, FieldOperand(tmp2, Map::kInstanceTypeOffset));
+ __ movq(tmp3, tmp1);
+ STATIC_ASSERT(kNotStringTag != 0);
+ __ or_(tmp3, tmp2);
+ __ testb(tmp3, Immediate(kIsNotStringMask));
+ __ j(not_zero, &miss);
+
+ // Fast check for identical strings.
+ Label not_same;
+ __ cmpq(left, right);
+ __ j(not_equal, ¬_same, Label::kNear);
+ STATIC_ASSERT(EQUAL == 0);
+ STATIC_ASSERT(kSmiTag == 0);
+ __ Move(rax, Smi::FromInt(EQUAL));
+ __ ret(0);
+
+ // Handle not identical strings.
+ __ bind(¬_same);
+
+ // Check that both strings are symbols. If they are, we're done
+ // because we already know they are not identical.
+ Label do_compare;
+ STATIC_ASSERT(kSymbolTag != 0);
+ __ and_(tmp1, tmp2);
+ __ testb(tmp1, Immediate(kIsSymbolMask));
+ __ j(zero, &do_compare, Label::kNear);
+ // Make sure rax is non-zero. At this point input operands are
+ // guaranteed to be non-zero.
+ ASSERT(right.is(rax));
+ __ ret(0);
+
+ // Check that both strings are sequential ASCII.
+ Label runtime;
+ __ bind(&do_compare);
+ __ JumpIfNotBothSequentialAsciiStrings(left, right, tmp1, tmp2, &runtime);
+
+ // Compare flat ASCII strings. Returns when done.
+ StringCompareStub::GenerateFlatAsciiStringEquals(
+ masm, left, right, tmp1, tmp2);
+
+ // Handle more complex cases in runtime.
+ __ bind(&runtime);
+ __ pop(tmp1); // Return address.
+ __ push(left);
+ __ push(right);
+ __ push(tmp1);
+ __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
+
+ __ bind(&miss);
+ GenerateMiss(masm);
+}
+
+
void ICCompareStub::GenerateObjects(MacroAssembler* masm) {
ASSERT(state_ == CompareIC::OBJECTS);
- NearLabel miss;
+ Label miss;
Condition either_smi = masm->CheckEitherSmi(rdx, rax);
- __ j(either_smi, &miss);
+ __ j(either_smi, &miss, Label::kNear);
__ CmpObjectType(rax, JS_OBJECT_TYPE, rcx);
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss, Label::kNear);
__ CmpObjectType(rdx, JS_OBJECT_TYPE, rcx);
- __ j(not_equal, &miss, not_taken);
+ __ j(not_equal, &miss, Label::kNear);
ASSERT(GetCondition() == equal);
__ subq(rax, rdx);
@@ -4510,6 +4928,206 @@
}
+MaybeObject* StringDictionaryLookupStub::GenerateNegativeLookup(
+ MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register properties,
+ String* name,
+ Register r0) {
+ // If names of slots in range from 1 to kProbes - 1 for the hash value are
+ // not equal to the name and kProbes-th slot is not used (its name is the
+ // undefined value), it guarantees the hash table doesn't contain the
+ // property. It's true even if some slots represent deleted properties
+ // (their names are the null value).
+ for (int i = 0; i < kInlinedProbes; i++) {
+ // r0 points to properties hash.
+ // Compute the masked index: (hash + i + i * i) & mask.
+ Register index = r0;
+ // Capacity is smi 2^n.
+ __ SmiToInteger32(index, FieldOperand(properties, kCapacityOffset));
+ __ decl(index);
+ __ and_(index,
+ Immediate(name->Hash() + StringDictionary::GetProbeOffset(i)));
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(StringDictionary::kEntrySize == 3);
+ __ lea(index, Operand(index, index, times_2, 0)); // index *= 3.
+
+ Register entity_name = r0;
+ // Having undefined at this place means the name is not contained.
+ ASSERT_EQ(kSmiTagSize, 1);
+ __ movq(entity_name, Operand(properties,
+ index,
+ times_pointer_size,
+ kElementsStartOffset - kHeapObjectTag));
+ __ Cmp(entity_name, masm->isolate()->factory()->undefined_value());
+ __ j(equal, done);
+
+ // Stop if found the property.
+ __ Cmp(entity_name, Handle<String>(name));
+ __ j(equal, miss);
+
+ // Check if the entry name is not a symbol.
+ __ movq(entity_name, FieldOperand(entity_name, HeapObject::kMapOffset));
+ __ testb(FieldOperand(entity_name, Map::kInstanceTypeOffset),
+ Immediate(kIsSymbolMask));
+ __ j(zero, miss);
+ }
+
+ StringDictionaryLookupStub stub(properties,
+ r0,
+ r0,
+ StringDictionaryLookupStub::NEGATIVE_LOOKUP);
+ __ Push(Handle<Object>(name));
+ __ push(Immediate(name->Hash()));
+ MaybeObject* result = masm->TryCallStub(&stub);
+ if (result->IsFailure()) return result;
+ __ testq(r0, r0);
+ __ j(not_zero, miss);
+ __ jmp(done);
+ return result;
+}
+
+
+// Probe the string dictionary in the |elements| register. Jump to the
+// |done| label if a property with the given name is found leaving the
+// index into the dictionary in |r1|. Jump to the |miss| label
+// otherwise.
+void StringDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register elements,
+ Register name,
+ Register r0,
+ Register r1) {
+ // Assert that name contains a string.
+ if (FLAG_debug_code) __ AbortIfNotString(name);
+
+ __ SmiToInteger32(r0, FieldOperand(elements, kCapacityOffset));
+ __ decl(r0);
+
+ for (int i = 0; i < kInlinedProbes; i++) {
+ // Compute the masked index: (hash + i + i * i) & mask.
+ __ movl(r1, FieldOperand(name, String::kHashFieldOffset));
+ __ shrl(r1, Immediate(String::kHashShift));
+ if (i > 0) {
+ __ addl(r1, Immediate(StringDictionary::GetProbeOffset(i)));
+ }
+ __ and_(r1, r0);
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(StringDictionary::kEntrySize == 3);
+ __ lea(r1, Operand(r1, r1, times_2, 0)); // r1 = r1 * 3
+
+ // Check if the key is identical to the name.
+ __ cmpq(name, Operand(elements, r1, times_pointer_size,
+ kElementsStartOffset - kHeapObjectTag));
+ __ j(equal, done);
+ }
+
+ StringDictionaryLookupStub stub(elements,
+ r0,
+ r1,
+ POSITIVE_LOOKUP);
+ __ push(name);
+ __ movl(r0, FieldOperand(name, String::kHashFieldOffset));
+ __ shrl(r0, Immediate(String::kHashShift));
+ __ push(r0);
+ __ CallStub(&stub);
+
+ __ testq(r0, r0);
+ __ j(zero, miss);
+ __ jmp(done);
+}
+
+
+void StringDictionaryLookupStub::Generate(MacroAssembler* masm) {
+ // Stack frame on entry:
+ // esp[0 * kPointerSize]: return address.
+ // esp[1 * kPointerSize]: key's hash.
+ // esp[2 * kPointerSize]: key.
+ // Registers:
+ // dictionary_: StringDictionary to probe.
+ // result_: used as scratch.
+ // index_: will hold an index of entry if lookup is successful.
+ // might alias with result_.
+ // Returns:
+ // result_ is zero if lookup failed, non zero otherwise.
+
+ Label in_dictionary, maybe_in_dictionary, not_in_dictionary;
+
+ Register scratch = result_;
+
+ __ SmiToInteger32(scratch, FieldOperand(dictionary_, kCapacityOffset));
+ __ decl(scratch);
+ __ push(scratch);
+
+ // If names of slots in range from 1 to kProbes - 1 for the hash value are
+ // not equal to the name and kProbes-th slot is not used (its name is the
+ // undefined value), it guarantees the hash table doesn't contain the
+ // property. It's true even if some slots represent deleted properties
+ // (their names are the null value).
+ for (int i = kInlinedProbes; i < kTotalProbes; i++) {
+ // Compute the masked index: (hash + i + i * i) & mask.
+ __ movq(scratch, Operand(rsp, 2 * kPointerSize));
+ if (i > 0) {
+ __ addl(scratch, Immediate(StringDictionary::GetProbeOffset(i)));
+ }
+ __ and_(scratch, Operand(rsp, 0));
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(StringDictionary::kEntrySize == 3);
+ __ lea(index_, Operand(scratch, scratch, times_2, 0)); // index *= 3.
+
+ // Having undefined at this place means the name is not contained.
+ __ movq(scratch, Operand(dictionary_,
+ index_,
+ times_pointer_size,
+ kElementsStartOffset - kHeapObjectTag));
+
+ __ Cmp(scratch, masm->isolate()->factory()->undefined_value());
+ __ j(equal, ¬_in_dictionary);
+
+ // Stop if found the property.
+ __ cmpq(scratch, Operand(rsp, 3 * kPointerSize));
+ __ j(equal, &in_dictionary);
+
+ if (i != kTotalProbes - 1 && mode_ == NEGATIVE_LOOKUP) {
+ // If we hit a non symbol key during negative lookup
+ // we have to bailout as this key might be equal to the
+ // key we are looking for.
+
+ // Check if the entry name is not a symbol.
+ __ movq(scratch, FieldOperand(scratch, HeapObject::kMapOffset));
+ __ testb(FieldOperand(scratch, Map::kInstanceTypeOffset),
+ Immediate(kIsSymbolMask));
+ __ j(zero, &maybe_in_dictionary);
+ }
+ }
+
+ __ bind(&maybe_in_dictionary);
+ // If we are doing negative lookup then probing failure should be
+ // treated as a lookup success. For positive lookup probing failure
+ // should be treated as lookup failure.
+ if (mode_ == POSITIVE_LOOKUP) {
+ __ movq(scratch, Immediate(0));
+ __ Drop(1);
+ __ ret(2 * kPointerSize);
+ }
+
+ __ bind(&in_dictionary);
+ __ movq(scratch, Immediate(1));
+ __ Drop(1);
+ __ ret(2 * kPointerSize);
+
+ __ bind(¬_in_dictionary);
+ __ movq(scratch, Immediate(0));
+ __ Drop(1);
+ __ ret(2 * kPointerSize);
+}
+
+
#undef __
} } // namespace v8::internal