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:
parent
7b89a5f2b8
commit
71548e969b
@ -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) {
|
void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
@ -550,6 +550,9 @@ class CodeGenerator: public AstVisitor {
|
|||||||
|
|
||||||
void GenerateIsRegExpEquivalent(ZoneList<Expression*>* args);
|
void GenerateIsRegExpEquivalent(ZoneList<Expression*>* args);
|
||||||
|
|
||||||
|
void GenerateHasCachedArrayIndex(ZoneList<Expression*>* args);
|
||||||
|
void GenerateGetCachedArrayIndex(ZoneList<Expression*>* args);
|
||||||
|
|
||||||
// Simple condition analysis.
|
// Simple condition analysis.
|
||||||
enum ConditionAnalysis {
|
enum ConditionAnalysis {
|
||||||
ALWAYS_TRUE,
|
ALWAYS_TRUE,
|
||||||
|
@ -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) {
|
void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
|
||||||
Handle<String> name = expr->name();
|
Handle<String> name = expr->name();
|
||||||
if (name->length() > 0 && name->Get(0) == '_') {
|
if (name->length() > 0 && name->Get(0) == '_') {
|
||||||
|
@ -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.
|
// Defined in ic.cc.
|
||||||
Object* CallIC_Miss(Arguments args);
|
Object* CallIC_Miss(Arguments args);
|
||||||
|
|
||||||
@ -852,7 +826,7 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
|
|||||||
GenerateMiss(masm, argc);
|
GenerateMiss(masm, argc);
|
||||||
|
|
||||||
__ bind(&index_string);
|
__ bind(&index_string);
|
||||||
GenerateIndexFromHash(masm, r2, r3);
|
__ IndexFromHash(r3, r2);
|
||||||
// Now jump to the place where smi keys are handled.
|
// Now jump to the place where smi keys are handled.
|
||||||
__ jmp(&index_smi);
|
__ jmp(&index_smi);
|
||||||
}
|
}
|
||||||
@ -1249,7 +1223,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
|
|||||||
__ Ret();
|
__ Ret();
|
||||||
|
|
||||||
__ bind(&index_string);
|
__ bind(&index_string);
|
||||||
GenerateIndexFromHash(masm, key, r3);
|
__ IndexFromHash(r3, key);
|
||||||
// Now jump to the place where smi keys are handled.
|
// Now jump to the place where smi keys are handled.
|
||||||
__ jmp(&index_smi);
|
__ jmp(&index_smi);
|
||||||
}
|
}
|
||||||
|
@ -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,
|
void MacroAssembler::IntegerToDoubleConversionWithVFP3(Register inReg,
|
||||||
Register outHighReg,
|
Register outHighReg,
|
||||||
Register outLowReg) {
|
Register outLowReg) {
|
||||||
|
@ -469,6 +469,12 @@ class MacroAssembler: public Assembler {
|
|||||||
// occurred.
|
// occurred.
|
||||||
void IllegalOperation(int num_arguments);
|
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
|
// Get the number of least significant bits from a register
|
||||||
void GetLeastBitsFromSmi(Register dst, Register src, int num_least_bits);
|
void GetLeastBitsFromSmi(Register dst, Register src, int num_least_bits);
|
||||||
|
|
||||||
|
@ -116,7 +116,9 @@ enum UncatchableExceptionType { OUT_OF_MEMORY, TERMINATION };
|
|||||||
F(MathSin, 1, 1) \
|
F(MathSin, 1, 1) \
|
||||||
F(MathCos, 1, 1) \
|
F(MathCos, 1, 1) \
|
||||||
F(MathSqrt, 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
|
#if V8_TARGET_ARCH_IA32
|
||||||
|
@ -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) {
|
void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
|
||||||
ASSERT(!in_safe_int32_mode());
|
ASSERT(!in_safe_int32_mode());
|
||||||
if (CheckForInlineRuntimeCall(node)) {
|
if (CheckForInlineRuntimeCall(node)) {
|
||||||
|
@ -730,6 +730,9 @@ class CodeGenerator: public AstVisitor {
|
|||||||
// Check whether two RegExps are equivalent
|
// Check whether two RegExps are equivalent
|
||||||
void GenerateIsRegExpEquivalent(ZoneList<Expression*>* args);
|
void GenerateIsRegExpEquivalent(ZoneList<Expression*>* args);
|
||||||
|
|
||||||
|
void GenerateHasCachedArrayIndex(ZoneList<Expression*>* args);
|
||||||
|
void GenerateGetCachedArrayIndex(ZoneList<Expression*>* args);
|
||||||
|
|
||||||
// Simple condition analysis.
|
// Simple condition analysis.
|
||||||
enum ConditionAnalysis {
|
enum ConditionAnalysis {
|
||||||
ALWAYS_TRUE,
|
ALWAYS_TRUE,
|
||||||
|
@ -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) {
|
void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
|
||||||
Handle<String> name = expr->name();
|
Handle<String> name = expr->name();
|
||||||
if (name->length() > 0 && name->Get(0) == '_') {
|
if (name->length() > 0 && name->Get(0) == '_') {
|
||||||
|
@ -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) {
|
void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
|
||||||
// ----------- S t a t e -------------
|
// ----------- S t a t e -------------
|
||||||
// -- eax : key
|
// -- eax : key
|
||||||
@ -704,7 +679,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
|
|||||||
__ ret(0);
|
__ ret(0);
|
||||||
|
|
||||||
__ bind(&index_string);
|
__ bind(&index_string);
|
||||||
GenerateIndexFromHash(masm, eax, ebx);
|
__ IndexFromHash(ebx, eax);
|
||||||
// Now jump to the place where smi keys are handled.
|
// Now jump to the place where smi keys are handled.
|
||||||
__ jmp(&index_smi);
|
__ jmp(&index_smi);
|
||||||
}
|
}
|
||||||
@ -1565,7 +1540,7 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
|
|||||||
GenerateMiss(masm, argc);
|
GenerateMiss(masm, argc);
|
||||||
|
|
||||||
__ bind(&index_string);
|
__ bind(&index_string);
|
||||||
GenerateIndexFromHash(masm, ecx, ebx);
|
__ IndexFromHash(ebx, ecx);
|
||||||
// Now jump to the place where smi keys are handled.
|
// Now jump to the place where smi keys are handled.
|
||||||
__ jmp(&index_smi);
|
__ jmp(&index_smi);
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
void MacroAssembler::AbortIfSmi(Register object) {
|
||||||
test(object, Immediate(kSmiTagMask));
|
test(object, Immediate(kSmiTagMask));
|
||||||
Assert(not_equal, "Operand is a smi");
|
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) {
|
void MacroAssembler::CallRuntime(Runtime::FunctionId id, int num_arguments) {
|
||||||
CallRuntime(Runtime::FunctionForId(id), num_arguments);
|
CallRuntime(Runtime::FunctionForId(id), num_arguments);
|
||||||
}
|
}
|
||||||
|
@ -267,6 +267,9 @@ class MacroAssembler: public Assembler {
|
|||||||
// Abort execution if argument is a smi. Used in debug code.
|
// Abort execution if argument is a smi. Used in debug code.
|
||||||
void AbortIfSmi(Register object);
|
void AbortIfSmi(Register object);
|
||||||
|
|
||||||
|
// Abort execution if argument is a string. Used in debug code.
|
||||||
|
void AbortIfNotString(Register object);
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Exception handling
|
// Exception handling
|
||||||
|
|
||||||
@ -396,6 +399,12 @@ class MacroAssembler: public Assembler {
|
|||||||
// occurred.
|
// occurred.
|
||||||
void IllegalOperation(int num_arguments);
|
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
|
// Runtime calls
|
||||||
|
|
||||||
|
@ -4854,6 +4854,19 @@ static Object* Runtime_StringToNumber(Arguments args) {
|
|||||||
if (minus) {
|
if (minus) {
|
||||||
if (d == 0) return Heap::minus_zero_value();
|
if (d == 0) return Heap::minus_zero_value();
|
||||||
d = -d;
|
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);
|
return Smi::FromInt(d);
|
||||||
}
|
}
|
||||||
|
@ -502,7 +502,10 @@ function ToBoolean(x) {
|
|||||||
// ECMA-262, section 9.3, page 31.
|
// ECMA-262, section 9.3, page 31.
|
||||||
function ToNumber(x) {
|
function ToNumber(x) {
|
||||||
if (IS_NUMBER(x)) return 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_BOOLEAN(x)) return x ? 1 : 0;
|
||||||
if (IS_UNDEFINED(x)) return $NaN;
|
if (IS_UNDEFINED(x)) return $NaN;
|
||||||
return (IS_NULL(x)) ? 0 : ToNumber(%DefaultNumber(x));
|
return (IS_NULL(x)) ? 0 : ToNumber(%DefaultNumber(x));
|
||||||
|
@ -111,13 +111,20 @@ function GlobalParseInt(string, radix) {
|
|||||||
if (!(radix == 0 || (2 <= radix && radix <= 36)))
|
if (!(radix == 0 || (2 <= radix && radix <= 36)))
|
||||||
return $NaN;
|
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
|
// ECMA-262 - 15.1.2.3
|
||||||
function GlobalParseFloat(string) {
|
function GlobalParseFloat(string) {
|
||||||
return %StringParseFloat(ToString(string));
|
string = TO_STRING_INLINE(string);
|
||||||
|
if (%_HasCachedArrayIndex(string)) return %_GetCachedArrayIndex(string);
|
||||||
|
return %StringParseFloat(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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) {
|
void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
|
||||||
if (CheckForInlineRuntimeCall(node)) {
|
if (CheckForInlineRuntimeCall(node)) {
|
||||||
return;
|
return;
|
||||||
|
@ -683,6 +683,9 @@ class CodeGenerator: public AstVisitor {
|
|||||||
|
|
||||||
void GenerateIsRegExpEquivalent(ZoneList<Expression*>* args);
|
void GenerateIsRegExpEquivalent(ZoneList<Expression*>* args);
|
||||||
|
|
||||||
|
void GenerateHasCachedArrayIndex(ZoneList<Expression*>* args);
|
||||||
|
void GenerateGetCachedArrayIndex(ZoneList<Expression*>* args);
|
||||||
|
|
||||||
// Simple condition analysis.
|
// Simple condition analysis.
|
||||||
enum ConditionAnalysis {
|
enum ConditionAnalysis {
|
||||||
ALWAYS_TRUE,
|
ALWAYS_TRUE,
|
||||||
|
@ -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) {
|
void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
|
||||||
Handle<String> name = expr->name();
|
Handle<String> name = expr->name();
|
||||||
if (name->length() > 0 && name->Get(0) == '_') {
|
if (name->length() > 0 && name->Get(0) == '_') {
|
||||||
|
@ -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) {
|
void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
|
||||||
// ----------- S t a t e -------------
|
// ----------- S t a t e -------------
|
||||||
@ -743,7 +718,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
|
|||||||
__ ret(0);
|
__ ret(0);
|
||||||
|
|
||||||
__ bind(&index_string);
|
__ bind(&index_string);
|
||||||
GenerateIndexFromHash(masm, rax, rbx);
|
__ IndexFromHash(rbx, rax);
|
||||||
__ jmp(&index_smi);
|
__ jmp(&index_smi);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1599,7 +1574,7 @@ void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
|
|||||||
GenerateMiss(masm, argc);
|
GenerateMiss(masm, argc);
|
||||||
|
|
||||||
__ bind(&index_string);
|
__ bind(&index_string);
|
||||||
GenerateIndexFromHash(masm, rcx, rbx);
|
__ IndexFromHash(rbx, rcx);
|
||||||
// Now jump to the place where smi keys are handled.
|
// Now jump to the place where smi keys are handled.
|
||||||
__ jmp(&index_smi);
|
__ jmp(&index_smi);
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
void MacroAssembler::CallRuntime(Runtime::FunctionId id, int num_arguments) {
|
||||||
CallRuntime(Runtime::FunctionForId(id), num_arguments);
|
CallRuntime(Runtime::FunctionForId(id), num_arguments);
|
||||||
}
|
}
|
||||||
|
@ -715,6 +715,12 @@ class MacroAssembler: public Assembler {
|
|||||||
// occurred.
|
// occurred.
|
||||||
void IllegalOperation(int num_arguments);
|
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.
|
// Find the function context up the context chain.
|
||||||
void LoadContext(Register dst, int context_chain_length);
|
void LoadContext(Register dst, int context_chain_length);
|
||||||
|
|
||||||
|
@ -176,7 +176,11 @@ var knownProblems = {
|
|||||||
"_GetFromCache": true,
|
"_GetFromCache": true,
|
||||||
|
|
||||||
// This function expects its first argument to be a non-smi.
|
// 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 = {
|
var currentlyUncallable = {
|
||||||
|
Loading…
Reference in New Issue
Block a user