Chromium Code Reviews| Index: src/hydrogen.cc |
| diff --git a/src/hydrogen.cc b/src/hydrogen.cc |
| index 14ac329575680c20e6cbac81e6298a9f6ef914da..239a1aa046915bbd291fffa1acc2583e31571cc4 100644 |
| --- a/src/hydrogen.cc |
| +++ b/src/hydrogen.cc |
| @@ -1283,9 +1283,63 @@ void HGraphBuilder::BuildTransitionElementsKind(HValue* object, |
| } |
| -HValue* HGraphBuilder::BuildLookupNumberStringCache( |
| - HValue* object, |
| - HIfContinuation* continuation) { |
| +HValue* HGraphBuilder::BuildSmiToString(HValue* number) { |
| + // Everything in here is safe wrt. observable side effects. |
|
rossberg
2013/10/17 11:02:19
Nit: this comment seems redundant, the next line s
Benedikt Meurer
2013/10/18 06:53:05
Done.
|
| + NoObservableSideEffectsScope scope(this); |
| + |
| + // Load the number string cache. |
| + HValue* number_string_cache = |
| + Add<HLoadRoot>(Heap::kNumberStringCacheRootIndex); |
| + |
| + // Make the hash maks from the length of the number string cache. It |
|
rossberg
2013/10/17 11:02:19
Typo: mask
Benedikt Meurer
2013/10/18 06:53:05
Done.
|
| + // contains two elements (number and string) for each cache entry. |
| + HValue* mask = AddLoadFixedArrayLength(number_string_cache); |
| + mask->set_type(HType::Smi()); |
| + mask = Add<HSar>(mask, graph()->GetConstant1()); |
| + mask = Add<HSub>(mask, graph()->GetConstant1()); |
| + |
| + // Compute hash for smi similar to smi_get_hash(). |
| + HValue* hash = Add<HBitwise>(Token::BIT_AND, number, mask); |
| + |
| + // Load the key. |
| + HValue* key_index = Add<HShl>(hash, graph()->GetConstant1()); |
| + HValue* key = Add<HLoadKeyed>(number_string_cache, key_index, |
| + static_cast<HValue*>(NULL), |
| + FAST_ELEMENTS, ALLOW_RETURN_HOLE); |
| + |
| + // Check if number == key. |
| + IfBuilder if_numberiskey(this); |
| + if_numberiskey.If<HCompareObjectEqAndBranch>(number, key); |
| + if_numberiskey.Then(); |
| + { |
| + // Count number to string operation in native code. |
| + AddIncrementCounter(isolate()->counters()->number_to_string_native()); |
| + |
| + // Load the value in case of cache hit. |
| + HValue* value_index = Add<HAdd>(key_index, graph()->GetConstant1()); |
| + Push(Add<HLoadKeyed>(number_string_cache, value_index, |
| + static_cast<HValue*>(NULL), |
| + FAST_ELEMENTS, ALLOW_RETURN_HOLE)); |
| + } |
| + if_numberiskey.Else(); |
| + { |
| + // Cache miss, fallback to runtime. |
| + Add<HPushArgument>(number); |
| + Push(Add<HCallRuntime>( |
| + isolate()->factory()->empty_string(), |
| + Runtime::FunctionForId(Runtime::kNumberToStringSkipCache), |
| + 1)); |
| + } |
| + if_numberiskey.End(); |
| + |
| + return Pop(); |
| +} |
| + |
| + |
| +HValue* HGraphBuilder::BuildNumberToString(HValue* number) { |
| + // Everything in here is safe wrt. observable side effects. |
|
rossberg
2013/10/17 11:02:19
Nit: as above
Benedikt Meurer
2013/10/18 06:53:05
Done.
|
| + NoObservableSideEffectsScope scope(this); |
| + |
| // Create a joinable continuation. |
| HIfContinuation found(graph()->CreateBasicBlock(), |
| graph()->CreateBasicBlock()); |
| @@ -1301,13 +1355,13 @@ HValue* HGraphBuilder::BuildLookupNumberStringCache( |
| mask = Add<HSar>(mask, graph()->GetConstant1()); |
| mask = Add<HSub>(mask, graph()->GetConstant1()); |
| - // Check whether object is a smi. |
| - IfBuilder if_objectissmi(this); |
| - if_objectissmi.If<HIsSmiAndBranch>(object); |
| - if_objectissmi.Then(); |
| + // Check whether number is a smi. |
| + IfBuilder if_numberissmi(this); |
| + if_numberissmi.If<HIsSmiAndBranch>(number); |
| + if_numberissmi.Then(); |
| { |
|
oliv
2013/10/17 10:56:30
Please find a way to share the code with BuildSmiT
Benedikt Meurer
2013/10/18 06:53:05
Done.
|
| // Compute hash for smi similar to smi_get_hash(). |
| - HValue* hash = Add<HBitwise>(Token::BIT_AND, object, mask); |
| + HValue* hash = Add<HBitwise>(Token::BIT_AND, number, mask); |
| // Load the key. |
| HValue* key_index = Add<HShl>(hash, graph()->GetConstant1()); |
| @@ -1315,104 +1369,81 @@ HValue* HGraphBuilder::BuildLookupNumberStringCache( |
| static_cast<HValue*>(NULL), |
| FAST_ELEMENTS, ALLOW_RETURN_HOLE); |
| - // Check if object == key. |
| - IfBuilder if_objectiskey(this); |
| - if_objectiskey.If<HCompareObjectEqAndBranch>(key, object); |
| - if_objectiskey.Then(); |
| + // Check if number == key. |
| + IfBuilder if_numberiskey(this); |
| + if_numberiskey.If<HCompareObjectEqAndBranch>(number, key); |
| + if_numberiskey.Then(); |
| { |
| // Make the key_index available. |
| Push(key_index); |
| } |
| - if_objectiskey.JoinContinuation(&found); |
| + if_numberiskey.JoinContinuation(&found); |
| } |
| - if_objectissmi.Else(); |
| + if_numberissmi.Else(); |
| { |
| - // Check if object is a heap number. |
| - IfBuilder if_objectisnumber(this); |
| - if_objectisnumber.If<HCompareMap>( |
| - object, isolate()->factory()->heap_number_map()); |
| - if_objectisnumber.Then(); |
| + // Check that the number is actually a heap number. |
| + BuildCheckMap(number, isolate()->factory()->heap_number_map()); |
| + |
| + // Compute hash for heap number similar to double_get_hash(). |
| + HValue* low = Add<HLoadNamedField>( |
| + number, HObjectAccess::ForHeapNumberValueLowestBits()); |
| + HValue* high = Add<HLoadNamedField>( |
| + number, HObjectAccess::ForHeapNumberValueHighestBits()); |
| + HValue* hash = Add<HBitwise>(Token::BIT_XOR, low, high); |
| + hash = Add<HBitwise>(Token::BIT_AND, hash, mask); |
| + |
| + // Load the key. |
| + HValue* key_index = Add<HShl>(hash, graph()->GetConstant1()); |
| + HValue* key = Add<HLoadKeyed>(number_string_cache, key_index, |
| + static_cast<HValue*>(NULL), |
| + FAST_ELEMENTS, ALLOW_RETURN_HOLE); |
| + |
| + // Check if key is a heap number (the number string cache contains only |
| + // SMIs and heap number, so it is sufficient to do a SMI check here). |
| + IfBuilder if_keyisnotsmi(this); |
| + if_keyisnotsmi.IfNot<HIsSmiAndBranch>(key); |
| + if_keyisnotsmi.Then(); |
| { |
| - // Compute hash for heap number similar to double_get_hash(). |
| - HValue* low = Add<HLoadNamedField>( |
| - object, HObjectAccess::ForHeapNumberValueLowestBits()); |
| - HValue* high = Add<HLoadNamedField>( |
| - object, HObjectAccess::ForHeapNumberValueHighestBits()); |
| - HValue* hash = Add<HBitwise>(Token::BIT_XOR, low, high); |
| - hash = Add<HBitwise>(Token::BIT_AND, hash, mask); |
| - |
| - // Load the key. |
| - HValue* key_index = Add<HShl>(hash, graph()->GetConstant1()); |
| - HValue* key = Add<HLoadKeyed>(number_string_cache, key_index, |
| - static_cast<HValue*>(NULL), |
| - FAST_ELEMENTS, ALLOW_RETURN_HOLE); |
| - |
| - // Check if key is a heap number. |
| - IfBuilder if_keyisnumber(this); |
| - if_keyisnumber.IfNot<HIsSmiAndBranch>(key); |
| - if_keyisnumber.AndIf<HCompareMap>( |
| - key, isolate()->factory()->heap_number_map()); |
| - if_keyisnumber.Then(); |
| + // Check if values of key and object match. |
| + IfBuilder if_keyeqnumber(this); |
| + if_keyeqnumber.If<HCompareNumericAndBranch>( |
| + Add<HLoadNamedField>(key, HObjectAccess::ForHeapNumberValue()), |
| + Add<HLoadNamedField>(number, HObjectAccess::ForHeapNumberValue()), |
| + Token::EQ); |
| + if_keyeqnumber.Then(); |
| { |
| - // Check if values of key and object match. |
| - IfBuilder if_keyeqobject(this); |
| - if_keyeqobject.If<HCompareNumericAndBranch>( |
| - Add<HLoadNamedField>(key, HObjectAccess::ForHeapNumberValue()), |
| - Add<HLoadNamedField>(object, HObjectAccess::ForHeapNumberValue()), |
| - Token::EQ); |
| - if_keyeqobject.Then(); |
| - { |
| - // Make the key_index available. |
| - Push(key_index); |
| - } |
| - if_keyeqobject.JoinContinuation(&found); |
| + // Make the key_index available. |
| + Push(key_index); |
| } |
| - if_keyisnumber.JoinContinuation(&found); |
| + if_keyeqnumber.JoinContinuation(&found); |
| } |
| - if_objectisnumber.JoinContinuation(&found); |
| + if_keyisnotsmi.JoinContinuation(&found); |
| } |
| - if_objectissmi.End(); |
| + if_numberissmi.End(); |
| // Check for cache hit. |
| IfBuilder if_found(this, &found); |
| if_found.Then(); |
| + { |
| + // Count number to string operation in native code. |
| + AddIncrementCounter(isolate()->counters()->number_to_string_native()); |
| - // Load the value in case of cache hit. |
| - HValue* key_index = Pop(); |
| - HValue* value_index = Add<HAdd>(key_index, graph()->GetConstant1()); |
| - HValue* value = Add<HLoadKeyed>(number_string_cache, value_index, |
| - static_cast<HValue*>(NULL), |
| - FAST_ELEMENTS, ALLOW_RETURN_HOLE); |
| - AddIncrementCounter(isolate()->counters()->number_to_string_native()); |
| - |
| - if_found.CaptureContinuation(continuation); |
| - |
| - // The value is only available in true branch of continuation. |
| - return value; |
| -} |
| - |
| - |
| -HValue* HGraphBuilder::BuildNumberToString(HValue* number) { |
| - NoObservableSideEffectsScope scope(this); |
| - |
| - // Lookup the number in the number string cache. |
| - HIfContinuation continuation; |
| - HValue* value = BuildLookupNumberStringCache(number, &continuation); |
| - IfBuilder if_found(this, &continuation); |
| - if_found.Then(); |
| - |
| - // Cache hit. |
| - Push(value); |
| - |
| + // Load the value in case of cache hit. |
| + HValue* key_index = Pop(); |
| + HValue* value_index = Add<HAdd>(key_index, graph()->GetConstant1()); |
| + Push(Add<HLoadKeyed>(number_string_cache, value_index, |
| + static_cast<HValue*>(NULL), |
| + FAST_ELEMENTS, ALLOW_RETURN_HOLE)); |
| + } |
| if_found.Else(); |
| - |
| - // Cache miss, fallback to runtime. |
| - Add<HPushArgument>(number); |
| - Push(Add<HCallRuntime>( |
| - isolate()->factory()->empty_string(), |
| - Runtime::FunctionForId(Runtime::kNumberToStringSkipCache), |
| - 1)); |
| - |
| + { |
| + // Cache miss, fallback to runtime. |
| + Add<HPushArgument>(number); |
| + Push(Add<HCallRuntime>( |
| + isolate()->factory()->empty_string(), |
| + Runtime::FunctionForId(Runtime::kNumberToStringSkipCache), |
| + 1)); |
| + } |
| if_found.End(); |
| return Pop(); |
| @@ -7850,6 +7881,39 @@ HInstruction* HGraphBuilder::BuildBinaryOperation( |
| right_rep = Representation::FromType(right_type); |
| } |
| + // Special case for string addition here. |
| + if (op == Token::ADD && |
| + (left_type->Is(Type::String()) || |
| + right_type->Is(Type::String()))) { |
| + StringAddFlags flags = STRING_ADD_CHECK_NONE; |
| + |
| + if (left_type->Is(Type::String())) { |
| + BuildCheckHeapObject(left); |
| + AddInstruction(HCheckInstanceType::NewIsString(left, zone())); |
| + } else if (left_type->Is(Type::Smi())) { |
| + left = BuildSmiToString(EnforceNumberType(left, left_type)); |
| + } else if (left_type->Is(Type::Number())) { |
| + left = BuildNumberToString(left); |
| + } else { |
| + ASSERT_EQ(STRING_ADD_CHECK_NONE, flags); |
| + flags = STRING_ADD_CHECK_LEFT; |
| + } |
| + |
| + if (right_type->Is(Type::String())) { |
| + BuildCheckHeapObject(right); |
| + AddInstruction(HCheckInstanceType::NewIsString(right, zone())); |
| + } else if (right_type->Is(Type::Smi())) { |
| + right = BuildSmiToString(EnforceNumberType(right, right_type)); |
| + } else if (right_type->Is(Type::Number())) { |
| + right = BuildNumberToString(right); |
| + } else { |
| + ASSERT_EQ(STRING_ADD_CHECK_NONE, flags); |
| + flags = STRING_ADD_CHECK_RIGHT; |
| + } |
| + |
| + return NewUncasted<HStringAdd>(left, right, flags); |
| + } |
| + |
| if (binop_stub) { |
| left = EnforceNumberType(left, left_type); |
| right = EnforceNumberType(right, right_type); |
| @@ -7859,15 +7923,12 @@ HInstruction* HGraphBuilder::BuildBinaryOperation( |
| bool is_non_primitive = (left_rep.IsTagged() && !left_rep.IsSmi()) || |
| (right_rep.IsTagged() && !right_rep.IsSmi()); |
| - bool is_string_add = op == Token::ADD && |
| - (left_type->Is(Type::String()) || |
| - right_type->Is(Type::String())); |
| HInstruction* instr = NULL; |
| // Only the stub is allowed to call into the runtime, since otherwise we would |
| // inline several instructions (including the two pushes) for every tagged |
| // operation in optimized code, which is more expensive, than a stub call. |
| - if (binop_stub && is_non_primitive && !is_string_add) { |
| + if (binop_stub && is_non_primitive) { |
| HValue* function = AddLoadJSBuiltin(BinaryOpIC::TokenToJSBuiltin(op)); |
| Add<HPushArgument>(left); |
| Add<HPushArgument>(right); |
| @@ -7875,23 +7936,7 @@ HInstruction* HGraphBuilder::BuildBinaryOperation( |
| } else { |
| switch (op) { |
| case Token::ADD: |
| - if (is_string_add) { |
| - StringAddFlags flags = STRING_ADD_CHECK_BOTH; |
| - if (left_type->Is(Type::String())) { |
| - BuildCheckHeapObject(left); |
| - AddInstruction(HCheckInstanceType::NewIsString(left, zone())); |
| - flags = STRING_ADD_CHECK_RIGHT; |
| - } |
| - if (right_type->Is(Type::String())) { |
| - BuildCheckHeapObject(right); |
| - AddInstruction(HCheckInstanceType::NewIsString(right, zone())); |
| - flags = (flags == STRING_ADD_CHECK_BOTH) |
| - ? STRING_ADD_CHECK_LEFT : STRING_ADD_CHECK_NONE; |
| - } |
| - instr = NewUncasted<HStringAdd>(left, right, flags); |
| - } else { |
| - instr = NewUncasted<HAdd>(left, right); |
| - } |
| + instr = NewUncasted<HAdd>(left, right); |
| break; |
| case Token::SUB: |
| instr = NewUncasted<HSub>(left, right); |