Index: src/hydrogen.cc |
diff --git a/src/hydrogen.cc b/src/hydrogen.cc |
index 14ac329575680c20e6cbac81e6298a9f6ef914da..fbca58f0c6444161ca524c891ce0693eca1e236c 100644 |
--- a/src/hydrogen.cc |
+++ b/src/hydrogen.cc |
@@ -1283,9 +1283,10 @@ void HGraphBuilder::BuildTransitionElementsKind(HValue* object, |
} |
-HValue* HGraphBuilder::BuildLookupNumberStringCache( |
- HValue* object, |
- HIfContinuation* continuation) { |
+HValue* HGraphBuilder::BuildNumberToString(HValue* object, |
+ Handle<Type> type) { |
+ NoObservableSideEffectsScope scope(this); |
+ |
// Create a joinable continuation. |
HIfContinuation found(graph()->CreateBasicBlock(), |
graph()->CreateBasicBlock()); |
@@ -1294,7 +1295,7 @@ HValue* HGraphBuilder::BuildLookupNumberStringCache( |
HValue* number_string_cache = |
Add<HLoadRoot>(Heap::kNumberStringCacheRootIndex); |
- // Make the hash maks from the length of the number string cache. It |
+ // Make the hash mask from the length of the number string cache. It |
// contains two elements (number and string) for each cache entry. |
HValue* mask = AddLoadFixedArrayLength(number_string_cache); |
mask->set_type(HType::Smi()); |
@@ -1317,7 +1318,7 @@ HValue* HGraphBuilder::BuildLookupNumberStringCache( |
// Check if object == key. |
IfBuilder if_objectiskey(this); |
- if_objectiskey.If<HCompareObjectEqAndBranch>(key, object); |
+ if_objectiskey.If<HCompareObjectEqAndBranch>(object, key); |
if_objectiskey.Then(); |
{ |
// Make the key_index available. |
@@ -1327,92 +1328,84 @@ HValue* HGraphBuilder::BuildLookupNumberStringCache( |
} |
if_objectissmi.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(); |
- { |
- // 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(); |
+ if (type->Is(Type::Smi())) { |
+ if_objectissmi.Deopt("Excepted smi"); |
+ } else { |
+ // Check if the object is a heap number. |
+ IfBuilder if_objectisnumber(this); |
+ if_objectisnumber.If<HCompareMap>( |
+ object, isolate()->factory()->heap_number_map()); |
+ if_objectisnumber.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(); |
+ // 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 (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(); |
{ |
- // Make the key_index available. |
- Push(key_index); |
+ // 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); |
+ } |
+ if_keyisnotsmi.JoinContinuation(&found); |
+ } |
+ if_objectisnumber.Else(); |
+ { |
+ if (type->Is(Type::Number())) { |
+ if_objectisnumber.Deopt("Expected heap number"); |
} |
- if_keyeqobject.JoinContinuation(&found); |
} |
- if_keyisnumber.JoinContinuation(&found); |
+ if_objectisnumber.JoinContinuation(&found); |
} |
- if_objectisnumber.JoinContinuation(&found); |
} |
- if_objectissmi.End(); |
+ if_objectissmi.JoinContinuation(&found); |
// 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>(object); |
+ Push(Add<HCallRuntime>( |
+ isolate()->factory()->empty_string(), |
+ Runtime::FunctionForId(Runtime::kNumberToStringSkipCache), |
+ 1)); |
+ } |
if_found.End(); |
return Pop(); |
@@ -7850,6 +7843,42 @@ 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()))) { |
+ if (left_type->Is(Type::String())) { |
+ IfBuilder if_isstring(this); |
+ if_isstring.If<HIsStringAndBranch>(left); |
+ if_isstring.Then(); |
+ if_isstring.ElseDeopt("Expected string for LHS of binary operation"); |
+ } else if (left_type->Is(Type::Number())) { |
+ left = BuildNumberToString(left, left_type); |
+ } else { |
+ ASSERT(right_type->Is(Type::String())); |
+ HValue* function = AddLoadJSBuiltin(Builtins::STRING_ADD_RIGHT); |
+ Add<HPushArgument>(left); |
+ Add<HPushArgument>(right); |
+ return NewUncasted<HInvokeFunction>(function, 2); |
+ } |
+ |
+ if (right_type->Is(Type::String())) { |
+ IfBuilder if_isstring(this); |
+ if_isstring.If<HIsStringAndBranch>(right); |
+ if_isstring.Then(); |
+ if_isstring.ElseDeopt("Expected string for RHS of binary operation"); |
+ } else if (right_type->Is(Type::Number())) { |
+ right = BuildNumberToString(right, right_type); |
+ } else { |
+ ASSERT(left_type->Is(Type::String())); |
+ HValue* function = AddLoadJSBuiltin(Builtins::STRING_ADD_LEFT); |
+ Add<HPushArgument>(left); |
+ Add<HPushArgument>(right); |
+ return NewUncasted<HInvokeFunction>(function, 2); |
+ } |
+ |
+ return NewUncasted<HStringAdd>(left, right, STRING_ADD_CHECK_NONE); |
+ } |
+ |
if (binop_stub) { |
left = EnforceNumberType(left, left_type); |
right = EnforceNumberType(right, right_type); |
@@ -7859,15 +7888,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 +7901,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); |
@@ -9090,7 +9100,8 @@ void HOptimizedGraphBuilder::GenerateNumberToString(CallRuntime* call) { |
ASSERT_EQ(1, call->arguments()->length()); |
CHECK_ALIVE(VisitForValue(call->arguments()->at(0))); |
HValue* number = Pop(); |
- HValue* result = BuildNumberToString(number); |
+ HValue* result = BuildNumberToString( |
+ number, handle(Type::Number(), isolate())); |
return ast_context()->ReturnValue(result); |
} |