}
+void CodeGenerator::GenerateHasCachedArrayIndex(ZoneList<Expression*>* args) {
+ ASSERT(args->length() == 1);
+ Load(args->at(0));
+ Register value = frame_->PopToRegister();
+ Register tmp = frame_->scratch0();
+ __ ldr(tmp, FieldMemOperand(value, String::kHashFieldOffset));
+ __ tst(tmp, Operand(String::kContainsCachedArrayIndexMask));
+ cc_reg_ = eq;
+}
+
+
+void CodeGenerator::GenerateGetCachedArrayIndex(ZoneList<Expression*>* args) {
+ ASSERT(args->length() == 1);
+ Load(args->at(0));
+ Register value = frame_->PopToRegister();
+
+ __ ldr(value, FieldMemOperand(value, String::kHashFieldOffset));
+ __ IndexFromHash(value, value);
+ frame_->EmitPush(value);
+}
+
void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
#ifdef DEBUG
void GenerateIsRegExpEquivalent(ZoneList<Expression*>* args);
+ void GenerateHasCachedArrayIndex(ZoneList<Expression*>* args);
+ void GenerateGetCachedArrayIndex(ZoneList<Expression*>* args);
+
// Simple condition analysis.
enum ConditionAnalysis {
ALWAYS_TRUE,
}
+void FullCodeGenerator::EmitHasCachedArrayIndex(ZoneList<Expression*>* args) {
+ VisitForValue(args->at(0), kAccumulator);
+
+ Label materialize_true, materialize_false;
+ Label* if_true = NULL;
+ Label* if_false = NULL;
+ Label* fall_through = NULL;
+ PrepareTest(&materialize_true, &materialize_false,
+ &if_true, &if_false, &fall_through);
+
+ __ ldr(r0, FieldMemOperand(r0, String::kHashFieldOffset));
+ __ tst(r0, Operand(String::kContainsCachedArrayIndexMask));
+
+ __ b(eq, if_true);
+ __ b(if_false);
+
+ Apply(context_, if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) {
+ ASSERT(args->length() == 1);
+ VisitForValue(args->at(0), kAccumulator);
+ __ ldr(r0, FieldMemOperand(r0, String::kHashFieldOffset));
+ __ IndexFromHash(r0, r0);
+ Apply(context_, r0);
+}
+
+
void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
Handle<String> name = expr->name();
if (name->length() > 0 && name->Get(0) == '_') {
}
-// Picks out an array index from the hash field.
-static void GenerateIndexFromHash(MacroAssembler* masm,
- Register key,
- Register hash) {
- // Register use:
- // key - holds the overwritten key on exit.
- // hash - holds the key's hash. Clobbered.
-
- // If the hash field contains an array index pick it out. The assert checks
- // that the constants for the maximum number of digits for an array index
- // cached in the hash field and the number of bits reserved for it does not
- // conflict.
- ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
- (1 << String::kArrayIndexValueBits));
- // We want the smi-tagged index in key. kArrayIndexValueMask has zeros in
- // the low kHashShift bits.
- ASSERT(String::kHashShift >= kSmiTagSize);
- // Here we actually clobber the key which will be used if calling into
- // runtime later. However as the new key is the numeric value of a string key
- // there is no difference in using either key.
- ASSERT(String::kHashShift >= kSmiTagSize);
- __ Ubfx(hash, hash, String::kHashShift, String::kArrayIndexValueBits);
- __ mov(key, Operand(hash, LSL, kSmiTagSize));
-}
-
-
// Defined in ic.cc.
Object* CallIC_Miss(Arguments args);
GenerateMiss(masm, argc);
__ bind(&index_string);
- GenerateIndexFromHash(masm, r2, r3);
+ __ IndexFromHash(r3, r2);
// Now jump to the place where smi keys are handled.
__ jmp(&index_smi);
}
__ Ret();
__ bind(&index_string);
- GenerateIndexFromHash(masm, key, r3);
+ __ IndexFromHash(r3, key);
// Now jump to the place where smi keys are handled.
__ jmp(&index_smi);
}
}
+void MacroAssembler::IndexFromHash(Register hash, Register index) {
+ // If the hash field contains an array index pick it out. The assert checks
+ // that the constants for the maximum number of digits for an array index
+ // cached in the hash field and the number of bits reserved for it does not
+ // conflict.
+ ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
+ (1 << String::kArrayIndexValueBits));
+ // We want the smi-tagged index in key. kArrayIndexValueMask has zeros in
+ // the low kHashShift bits.
+ STATIC_ASSERT(kSmiTag == 0);
+ Ubfx(hash, hash, String::kHashShift, String::kArrayIndexValueBits);
+ mov(index, Operand(hash, LSL, kSmiTagSize));
+}
+
+
void MacroAssembler::IntegerToDoubleConversionWithVFP3(Register inReg,
Register outHighReg,
Register outLowReg) {
// occurred.
void IllegalOperation(int num_arguments);
+ // Picks out an array index from the hash field.
+ // Register use:
+ // hash - holds the index's hash. Clobbered.
+ // index - holds the overwritten index on exit.
+ void IndexFromHash(Register hash, Register index);
+
// Get the number of least significant bits from a register
void GetLeastBitsFromSmi(Register dst, Register src, int num_least_bits);
F(MathSin, 1, 1) \
F(MathCos, 1, 1) \
F(MathSqrt, 1, 1) \
- F(IsRegExpEquivalent, 2, 1)
+ F(IsRegExpEquivalent, 2, 1) \
+ F(HasCachedArrayIndex, 1, 1) \
+ F(GetCachedArrayIndex, 1, 1)
#if V8_TARGET_ARCH_IA32
}
+void CodeGenerator::GenerateHasCachedArrayIndex(ZoneList<Expression*>* args) {
+ ASSERT(args->length() == 1);
+ Load(args->at(0));
+ Result value = frame_->Pop();
+ value.ToRegister();
+ ASSERT(value.is_valid());
+ if (FLAG_debug_code) {
+ __ AbortIfNotString(value.reg());
+ }
+
+ __ test(FieldOperand(value.reg(), String::kHashFieldOffset),
+ Immediate(String::kContainsCachedArrayIndexMask));
+
+ value.Unuse();
+ destination()->Split(zero);
+}
+
+
+void CodeGenerator::GenerateGetCachedArrayIndex(ZoneList<Expression*>* args) {
+ ASSERT(args->length() == 1);
+ Load(args->at(0));
+ Result string = frame_->Pop();
+ string.ToRegister();
+ if (FLAG_debug_code) {
+ __ AbortIfNotString(string.reg());
+ }
+
+ Result number = allocator()->Allocate();
+ ASSERT(number.is_valid());
+ __ mov(number.reg(), FieldOperand(string.reg(), String::kHashFieldOffset));
+ __ IndexFromHash(number.reg(), number.reg());
+ string.Unuse();
+ frame_->Push(&number);
+}
+
+
void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
ASSERT(!in_safe_int32_mode());
if (CheckForInlineRuntimeCall(node)) {
// Check whether two RegExps are equivalent
void GenerateIsRegExpEquivalent(ZoneList<Expression*>* args);
+ void GenerateHasCachedArrayIndex(ZoneList<Expression*>* args);
+ void GenerateGetCachedArrayIndex(ZoneList<Expression*>* args);
+
// Simple condition analysis.
enum ConditionAnalysis {
ALWAYS_TRUE,
}
+void FullCodeGenerator::EmitHasCachedArrayIndex(ZoneList<Expression*>* args) {
+ ASSERT(args->length() == 1);
+
+ VisitForValue(args->at(0), kAccumulator);
+
+ if (FLAG_debug_code) {
+ __ AbortIfNotString(eax);
+ }
+
+ Label materialize_true, materialize_false;
+ Label* if_true = NULL;
+ Label* if_false = NULL;
+ Label* fall_through = NULL;
+ PrepareTest(&materialize_true, &materialize_false,
+ &if_true, &if_false, &fall_through);
+
+ __ test(FieldOperand(eax, String::kHashFieldOffset),
+ Immediate(String::kContainsCachedArrayIndexMask));
+ Split(zero, if_true, if_false, fall_through);
+
+ Apply(context_, if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) {
+ ASSERT(args->length() == 1);
+
+ VisitForValue(args->at(0), kAccumulator);
+
+ if (FLAG_debug_code) {
+ __ AbortIfNotString(eax);
+ }
+
+ __ mov(eax, FieldOperand(eax, String::kHashFieldOffset));
+ __ IndexFromHash(eax, eax);
+
+ Apply(context_, eax);
+}
+
+
void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
Handle<String> name = expr->name();
if (name->length() > 0 && name->Get(0) == '_') {
}
-// Picks out an array index from the hash field.
-static void GenerateIndexFromHash(MacroAssembler* masm,
- Register key,
- Register hash) {
- // Register use:
- // key - holds the overwritten key on exit.
- // hash - holds the key's hash. Clobbered.
-
- // The assert checks that the constants for the maximum number of digits
- // for an array index cached in the hash field and the number of bits
- // reserved for it does not conflict.
- ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
- (1 << String::kArrayIndexValueBits));
- // We want the smi-tagged index in key. kArrayIndexValueMask has zeros in
- // the low kHashShift bits.
- ASSERT(String::kHashShift >= kSmiTagSize);
- __ and_(hash, String::kArrayIndexValueMask);
- __ shr(hash, String::kHashShift - kSmiTagSize);
- // Here we actually clobber the key which will be used if calling into
- // runtime later. However as the new key is the numeric value of a string key
- // there is no difference in using either key.
- __ mov(key, hash);
-}
-
-
void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : key
__ ret(0);
__ bind(&index_string);
- GenerateIndexFromHash(masm, eax, ebx);
+ __ IndexFromHash(ebx, eax);
// Now jump to the place where smi keys are handled.
__ jmp(&index_smi);
}
GenerateMiss(masm, argc);
__ bind(&index_string);
- GenerateIndexFromHash(masm, ecx, ebx);
+ __ IndexFromHash(ebx, ecx);
// Now jump to the place where smi keys are handled.
__ jmp(&index_smi);
}
}
+void MacroAssembler::AbortIfNotString(Register object) {
+ test(object, Immediate(kSmiTagMask));
+ Assert(not_equal, "Operand is not a string");
+ push(object);
+ mov(object, FieldOperand(object, HeapObject::kMapOffset));
+ CmpInstanceType(object, FIRST_NONSTRING_TYPE);
+ pop(object);
+ Assert(below, "Operand is not a string");
+}
+
+
void MacroAssembler::AbortIfSmi(Register object) {
test(object, Immediate(kSmiTagMask));
Assert(not_equal, "Operand is a smi");
}
+void MacroAssembler::IndexFromHash(Register hash, Register index) {
+ // The assert checks that the constants for the maximum number of digits
+ // for an array index cached in the hash field and the number of bits
+ // reserved for it does not conflict.
+ ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
+ (1 << String::kArrayIndexValueBits));
+ // We want the smi-tagged index in key. kArrayIndexValueMask has zeros in
+ // the low kHashShift bits.
+ and_(hash, String::kArrayIndexValueMask);
+ STATIC_ASSERT(String::kHashShift >= kSmiTagSize && kSmiTag == 0);
+ if (String::kHashShift > kSmiTagSize) {
+ shr(hash, String::kHashShift - kSmiTagSize);
+ }
+ if (!index.is(hash)) {
+ mov(index, hash);
+ }
+}
+
+
void MacroAssembler::CallRuntime(Runtime::FunctionId id, int num_arguments) {
CallRuntime(Runtime::FunctionForId(id), num_arguments);
}
// Abort execution if argument is a smi. Used in debug code.
void AbortIfSmi(Register object);
+ // Abort execution if argument is a string. Used in debug code.
+ void AbortIfNotString(Register object);
+
// ---------------------------------------------------------------------------
// Exception handling
// occurred.
void IllegalOperation(int num_arguments);
+ // Picks out an array index from the hash field.
+ // Register use:
+ // hash - holds the index's hash. Clobbered.
+ // index - holds the overwritten index on exit.
+ void IndexFromHash(Register hash, Register index);
+
// ---------------------------------------------------------------------------
// Runtime calls
if (minus) {
if (d == 0) return Heap::minus_zero_value();
d = -d;
+ } else if (!subject->HasHashCode() &&
+ len <= String::kMaxArrayIndexSize &&
+ (len == 1 || data[0] != '0')) {
+ // String hash is not calculated yet but all the data are present.
+ // Update the hash field to speed up sequential convertions.
+ uint32_t hash = StringHasher::MakeCachedArrayIndex(d, len);
+#ifdef DEBUG
+ ASSERT((hash & String::kContainsCachedArrayIndexMask) == 0);
+ subject->Hash(); // Force hash calculation.
+ ASSERT_EQ(static_cast<int>(subject->hash_field()),
+ static_cast<int>(hash));
+#endif
+ subject->set_hash_field(hash);
}
return Smi::FromInt(d);
}
// ECMA-262, section 9.3, page 31.
function ToNumber(x) {
if (IS_NUMBER(x)) return x;
- if (IS_STRING(x)) return %StringToNumber(x);
+ if (IS_STRING(x)) {
+ return %_HasCachedArrayIndex(x) ? %_GetCachedArrayIndex(x)
+ : %StringToNumber(x);
+ }
if (IS_BOOLEAN(x)) return x ? 1 : 0;
if (IS_UNDEFINED(x)) return $NaN;
return (IS_NULL(x)) ? 0 : ToNumber(%DefaultNumber(x));
if (!(radix == 0 || (2 <= radix && radix <= 36)))
return $NaN;
}
- return %StringParseInt(ToString(string), radix);
+ string = TO_STRING_INLINE(string);
+ if (%_HasCachedArrayIndex(string) &&
+ (radix == 0 || radix == 10)) {
+ return %_GetCachedArrayIndex(string);
+ }
+ return %StringParseInt(string, radix);
}
// ECMA-262 - 15.1.2.3
function GlobalParseFloat(string) {
- return %StringParseFloat(ToString(string));
+ string = TO_STRING_INLINE(string);
+ if (%_HasCachedArrayIndex(string)) return %_GetCachedArrayIndex(string);
+ return %StringParseFloat(string);
}
}
+void CodeGenerator::GenerateHasCachedArrayIndex(ZoneList<Expression*>* args) {
+ ASSERT(args->length() == 1);
+ Load(args->at(0));
+ Result value = frame_->Pop();
+ value.ToRegister();
+ ASSERT(value.is_valid());
+ __ testl(FieldOperand(value.reg(), String::kHashFieldOffset),
+ Immediate(String::kContainsCachedArrayIndexMask));
+ value.Unuse();
+ destination()->Split(zero);
+}
+
+
+void CodeGenerator::GenerateGetCachedArrayIndex(ZoneList<Expression*>* args) {
+ ASSERT(args->length() == 1);
+ Load(args->at(0));
+ Result string = frame_->Pop();
+ string.ToRegister();
+
+ Result number = allocator()->Allocate();
+ ASSERT(number.is_valid());
+ __ movl(number.reg(), FieldOperand(string.reg(), String::kHashFieldOffset));
+ __ IndexFromHash(number.reg(), number.reg());
+ string.Unuse();
+ frame_->Push(&number);
+}
+
+
void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
if (CheckForInlineRuntimeCall(node)) {
return;
void GenerateIsRegExpEquivalent(ZoneList<Expression*>* args);
+ void GenerateHasCachedArrayIndex(ZoneList<Expression*>* args);
+ void GenerateGetCachedArrayIndex(ZoneList<Expression*>* args);
+
// Simple condition analysis.
enum ConditionAnalysis {
ALWAYS_TRUE,
}
+void FullCodeGenerator::EmitHasCachedArrayIndex(ZoneList<Expression*>* args) {
+ ASSERT(args->length() == 1);
+
+ VisitForValue(args->at(0), kAccumulator);
+
+ Label materialize_true, materialize_false;
+ Label* if_true = NULL;
+ Label* if_false = NULL;
+ Label* fall_through = NULL;
+ PrepareTest(&materialize_true, &materialize_false,
+ &if_true, &if_false, &fall_through);
+
+ __ testl(FieldOperand(rax, String::kHashFieldOffset),
+ Immediate(String::kContainsCachedArrayIndexMask));
+ __ j(zero, if_true);
+ __ jmp(if_false);
+
+ Apply(context_, if_true, if_false);
+}
+
+
+void FullCodeGenerator::EmitGetCachedArrayIndex(ZoneList<Expression*>* args) {
+ ASSERT(args->length() == 1);
+
+ VisitForValue(args->at(0), kAccumulator);
+
+ __ movl(rax, FieldOperand(rax, String::kHashFieldOffset));
+ ASSERT(String::kHashShift >= kSmiTagSize);
+ __ IndexFromHash(rax, rax);
+
+ Apply(context_, rax);
+}
+
+
void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
Handle<String> name = expr->name();
if (name->length() > 0 && name->Get(0) == '_') {
}
-// Picks out an array index from the hash field.
-static void GenerateIndexFromHash(MacroAssembler* masm,
- Register key,
- Register hash) {
- // Register use:
- // key - holds the overwritten key on exit.
- // hash - holds the key's hash. Clobbered.
-
- // The assert checks that the constants for the maximum number of digits
- // for an array index cached in the hash field and the number of bits
- // reserved for it does not conflict.
- ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
- (1 << String::kArrayIndexValueBits));
- // We want the smi-tagged index in key. Even if we subsequently go to
- // the slow case, converting the key to a smi is always valid.
- // key: string key
- // hash: key's hash field, including its array index value.
- __ and_(hash, Immediate(String::kArrayIndexValueMask));
- __ shr(hash, Immediate(String::kHashShift));
- // Here we actually clobber the key which will be used if calling into
- // runtime later. However as the new key is the numeric value of a string key
- // there is no difference in using either key.
- __ Integer32ToSmi(key, hash);
-}
-
void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
// ----------- S t a t e -------------
__ ret(0);
__ bind(&index_string);
- GenerateIndexFromHash(masm, rax, rbx);
+ __ IndexFromHash(rbx, rax);
__ jmp(&index_smi);
}
GenerateMiss(masm, argc);
__ bind(&index_string);
- GenerateIndexFromHash(masm, rcx, rbx);
+ __ IndexFromHash(rbx, rcx);
// Now jump to the place where smi keys are handled.
__ jmp(&index_smi);
}
}
+void MacroAssembler::IndexFromHash(Register hash, Register index) {
+ // The assert checks that the constants for the maximum number of digits
+ // for an array index cached in the hash field and the number of bits
+ // reserved for it does not conflict.
+ ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
+ (1 << String::kArrayIndexValueBits));
+ // We want the smi-tagged index in key. Even if we subsequently go to
+ // the slow case, converting the key to a smi is always valid.
+ // key: string key
+ // hash: key's hash field, including its array index value.
+ and_(hash, Immediate(String::kArrayIndexValueMask));
+ shr(hash, Immediate(String::kHashShift));
+ // Here we actually clobber the key which will be used if calling into
+ // runtime later. However as the new key is the numeric value of a string key
+ // there is no difference in using either key.
+ Integer32ToSmi(index, hash);
+}
+
+
void MacroAssembler::CallRuntime(Runtime::FunctionId id, int num_arguments) {
CallRuntime(Runtime::FunctionForId(id), num_arguments);
}
// occurred.
void IllegalOperation(int num_arguments);
+ // Picks out an array index from the hash field.
+ // Register use:
+ // hash - holds the index's hash. Clobbered.
+ // index - holds the overwritten index on exit.
+ void IndexFromHash(Register hash, Register index);
+
// Find the function context up the context chain.
void LoadContext(Register dst, int context_chain_length);
"_GetFromCache": true,
// This function expects its first argument to be a non-smi.
- "_IsStringWrapperSafeForDefaultValueOf" : true
+ "_IsStringWrapperSafeForDefaultValueOf" : true,
+
+ // Only applicable to strings.
+ "_HasCachedArrayIndex": true,
+ "_GetCachedArrayIndex": true
};
var currentlyUncallable = {