Using array index hash code for string-to-number conversion.

Review URL: http://codereview.chromium.org/3141022

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@5362 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
serya@chromium.org 2010-08-27 11:47:12 +00:00
parent 7b89a5f2b8
commit 71548e969b
23 changed files with 322 additions and 87 deletions

View File

@ -5636,6 +5636,27 @@ void CodeGenerator::GenerateIsRegExpEquivalent(ZoneList<Expression*>* args) {
}
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

View File

@ -550,6 +550,9 @@ class CodeGenerator: public AstVisitor {
void GenerateIsRegExpEquivalent(ZoneList<Expression*>* args);
void GenerateHasCachedArrayIndex(ZoneList<Expression*>* args);
void GenerateGetCachedArrayIndex(ZoneList<Expression*>* args);
// Simple condition analysis.
enum ConditionAnalysis {
ALWAYS_TRUE,

View File

@ -2539,6 +2539,35 @@ void FullCodeGenerator::EmitIsRegExpEquivalent(ZoneList<Expression*>* args) {
}
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) == '_') {

View File

@ -527,32 +527,6 @@ static void GenerateKeyStringCheck(MacroAssembler* masm,
}
// 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);
@ -852,7 +826,7 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
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);
}
@ -1249,7 +1223,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
__ Ret();
__ bind(&index_string);
GenerateIndexFromHash(masm, key, r3);
__ IndexFromHash(r3, key);
// Now jump to the place where smi keys are handled.
__ jmp(&index_smi);
}

View File

@ -1257,6 +1257,21 @@ void MacroAssembler::IllegalOperation(int num_arguments) {
}
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) {

View File

@ -469,6 +469,12 @@ class MacroAssembler: public Assembler {
// 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);

View File

@ -116,7 +116,9 @@ enum UncatchableExceptionType { OUT_OF_MEMORY, TERMINATION };
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

View File

@ -7918,6 +7918,42 @@ void CodeGenerator::GenerateIsRegExpEquivalent(ZoneList<Expression*>* args) {
}
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)) {

View File

@ -730,6 +730,9 @@ class CodeGenerator: public AstVisitor {
// 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,

View File

@ -2848,6 +2848,46 @@ void FullCodeGenerator::EmitIsRegExpEquivalent(ZoneList<Expression*>* args) {
}
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) == '_') {

View File

@ -519,31 +519,6 @@ static void GenerateKeyStringCheck(MacroAssembler* masm,
}
// 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
@ -704,7 +679,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
__ 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);
}
@ -1565,7 +1540,7 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
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);
}

View File

@ -303,6 +303,17 @@ void MacroAssembler::AbortIfNotSmi(Register object) {
}
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");
@ -940,6 +951,25 @@ void MacroAssembler::IllegalOperation(int num_arguments) {
}
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);
}

View File

@ -267,6 +267,9 @@ class MacroAssembler: public Assembler {
// 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
@ -396,6 +399,12 @@ class MacroAssembler: public Assembler {
// 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

View File

@ -4854,6 +4854,19 @@ static Object* Runtime_StringToNumber(Arguments args) {
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);
}

View File

@ -502,7 +502,10 @@ function ToBoolean(x) {
// 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));

View File

@ -111,13 +111,20 @@ function GlobalParseInt(string, radix) {
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);
}

View File

@ -7223,6 +7223,34 @@ void CodeGenerator::GenerateIsRegExpEquivalent(ZoneList<Expression*>* args) {
}
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;

View File

@ -683,6 +683,9 @@ class CodeGenerator: public AstVisitor {
void GenerateIsRegExpEquivalent(ZoneList<Expression*>* args);
void GenerateHasCachedArrayIndex(ZoneList<Expression*>* args);
void GenerateGetCachedArrayIndex(ZoneList<Expression*>* args);
// Simple condition analysis.
enum ConditionAnalysis {
ALWAYS_TRUE,

View File

@ -2597,6 +2597,40 @@ void FullCodeGenerator::EmitIsRegExpEquivalent(ZoneList<Expression*>* args) {
}
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) == '_') {

View File

@ -572,31 +572,6 @@ static void GenerateKeyStringCheck(MacroAssembler* masm,
}
// 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 -------------
@ -743,7 +718,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
__ ret(0);
__ bind(&index_string);
GenerateIndexFromHash(masm, rax, rbx);
__ IndexFromHash(rbx, rax);
__ jmp(&index_smi);
}
@ -1599,7 +1574,7 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
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);
}

View File

@ -391,6 +391,25 @@ void MacroAssembler::IllegalOperation(int num_arguments) {
}
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);
}

View File

@ -715,6 +715,12 @@ class MacroAssembler: public Assembler {
// 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);

View File

@ -176,7 +176,11 @@ var knownProblems = {
"_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 = {