Index: src/hydrogen.cc |
diff --git a/src/hydrogen.cc b/src/hydrogen.cc |
index 307595256c626a056f70b09f4face412ec312507..e96a992e01f9b3bcd319993b403ac00287f26970 100644 |
--- a/src/hydrogen.cc |
+++ b/src/hydrogen.cc |
@@ -1454,6 +1454,346 @@ HValue* HGraphBuilder::BuildNumberToString(HValue* object, |
} |
+void HGraphBuilder::BuildCopySeqStringChars(HValue* src, |
+ HValue* src_offset, |
+ String::Encoding src_encoding, |
+ HValue* dst, |
+ HValue* dst_offset, |
+ String::Encoding dst_encoding, |
+ HValue* length) { |
+ ASSERT(dst_encoding != String::ONE_BYTE_ENCODING || |
+ src_encoding == String::ONE_BYTE_ENCODING); |
+ LoopBuilder loop(this, context(), LoopBuilder::kPostIncrement); |
+ HValue* index = loop.BeginBody(graph()->GetConstant0(), length, Token::LT); |
+ { |
+ HValue* src_index = Add<HAdd>(src_offset, index); |
+ HValue* value = Add<HSeqStringGetChar>(src_encoding, src, src_index); |
+ HValue* dst_index = Add<HAdd>(dst_offset, index); |
+ Add<HSeqStringSetChar>(dst_encoding, dst, dst_index, value); |
+ } |
+ loop.EndBody(); |
+} |
+ |
+ |
+HValue* HGraphBuilder::BuildUncheckedStringAdd(HValue* left, |
+ HValue* right, |
+ PretenureFlag pretenure_flag) { |
+ // Determine the string lengths. |
+ HValue* left_length = Add<HLoadNamedField>( |
+ left, HObjectAccess::ForStringLength()); |
+ HValue* right_length = Add<HLoadNamedField>( |
+ right, HObjectAccess::ForStringLength()); |
+ |
+ // Check if we concatenated the strings here, or if we have to resort to the |
+ // runtime function. |
+ HIfContinuation handled(graph()->CreateBasicBlock(), |
+ graph()->CreateBasicBlock()); |
+ |
+ // Check if both parameters do not exceed half the max string length, because |
+ // exceptionally long strings should be handled in the runtime. This may be |
+ // too pessimistic, but it's the only portable way to express this in Hydrogen |
+ // right now. |
mvstanton
2013/11/08 10:17:50
I appreciate the detailed comments on this long an
Benedikt Meurer
2013/11/08 13:21:08
Done.
|
+ HConstant* max_length = Add<HConstant>( |
+ static_cast<int32_t>(String::kMaxLength / 2)); |
+ IfBuilder if_nooverflow(this); |
+ if_nooverflow.If<HCompareNumericAndBranch>( |
+ left_length, max_length, Token::LTE); |
+ if_nooverflow.AndIf<HCompareNumericAndBranch>( |
+ right_length, max_length, Token::LTE); |
+ if_nooverflow.Then(); |
+ { |
+ // Determine the string instance types. |
+ HLoadNamedField* left_instance_type = Add<HLoadNamedField>( |
+ Add<HLoadNamedField>(left, HObjectAccess::ForMap()), |
+ HObjectAccess::ForMapInstanceType()); |
+ HLoadNamedField* right_instance_type = Add<HLoadNamedField>( |
+ Add<HLoadNamedField>(right, HObjectAccess::ForMap()), |
+ HObjectAccess::ForMapInstanceType()); |
+ |
+ // Compute the length of the resulting string. |
+ HValue* length = Add<HAdd>(left_length, right_length); |
+ |
+ // Check if we should create a cons string. |
+ IfBuilder if_createcons(this); |
+ if_createcons.If<HCompareNumericAndBranch>( |
+ length, |
+ Add<HConstant>(static_cast<int32_t>(ConsString::kMinLength)), |
+ Token::GTE); |
+ if_createcons.Then(); |
+ { |
+ // Allocate the cons string object. HAllocate does not care whether we |
+ // pass CONS_STRING_TYPE or CONS_ASCII_STRING_TYPE here, so we just use |
+ // CONS_STRING_TYPE here. Below we decide whether the cons string is |
+ // one-byte or two-byte and set the appropriate map. |
+ HAllocate* string = Add<HAllocate>(Add<HConstant>(ConsString::kSize), |
+ HType::String(), pretenure_flag, |
+ CONS_STRING_TYPE); |
+ |
+ // We create a one-byte cons string if |
+ // 1. both strings are one-byte, or |
+ // 2. at least one of the strings is two-byte, but happens to contain only |
+ // one-byte characters. |
+ // To do this, we check |
+ // 1. if both strings are one-byte, or |
+ // 2. if the one-byte data hint is set in both strings, or |
+ // 3. if one of the strings has the one-byte data hint set and the other |
+ // string is one-byte. |
+ IfBuilder if_onebyte(this); |
+ STATIC_ASSERT(kTwoByteStringTag == 0); |
+ if_onebyte.If<HCompareNumericAndBranch>( |
+ Add<HBitwise>( |
+ Token::BIT_AND, |
+ Add<HBitwise>( |
+ Token::BIT_AND, left_instance_type, right_instance_type), |
+ Add<HConstant>(static_cast<int32_t>(kStringEncodingMask))), |
+ graph()->GetConstant0(), |
+ Token::NE); |
+ if_onebyte.Or(); |
+ STATIC_ASSERT(kOneByteDataHintMask != 0); |
+ if_onebyte.If<HCompareNumericAndBranch>( |
+ Add<HBitwise>( |
+ Token::BIT_AND, |
+ Add<HBitwise>( |
+ Token::BIT_AND, left_instance_type, right_instance_type), |
+ Add<HConstant>(static_cast<int32_t>(kOneByteDataHintMask))), |
mvstanton
2013/11/08 10:17:50
These hurt my eyes. :)
How about a helper function
Benedikt Meurer
2013/11/08 13:21:08
I'm not sure that's going to make it better. Tried
|
+ graph()->GetConstant0(), |
+ Token::NE); |
+ if_onebyte.Or(); |
+ STATIC_ASSERT(kOneByteStringTag != 0 && |
+ kOneByteDataHintTag != 0 && |
+ kOneByteDataHintTag != kOneByteStringTag); |
+ if_onebyte.If<HCompareNumericAndBranch>( |
+ Add<HBitwise>( |
+ Token::BIT_AND, |
+ Add<HBitwise>( |
+ Token::BIT_XOR, left_instance_type, right_instance_type), |
+ Add<HConstant>(static_cast<int32_t>( |
+ kOneByteStringTag | kOneByteDataHintTag))), |
+ Add<HConstant>(static_cast<int32_t>( |
+ kOneByteStringTag | kOneByteDataHintTag)), |
+ Token::EQ); |
+ if_onebyte.Then(); |
+ { |
+ // We can safely skip the write barrier for storing the map here. |
+ HStoreNamedField* store_map = AddStoreMapConstant( |
+ string, isolate()->factory()->cons_ascii_string_map()); |
+ store_map->SkipWriteBarrier(); |
+ } |
+ if_onebyte.Else(); |
+ { |
+ // We can safely skip the write barrier for storing the map here. |
+ HStoreNamedField* store_map = AddStoreMapConstant( |
+ string, isolate()->factory()->cons_string_map()); |
+ store_map->SkipWriteBarrier(); |
+ } |
+ if_onebyte.End(); |
+ |
+ // Initialize the cons string fields. |
+ Add<HStoreNamedField>(string, HObjectAccess::ForStringHashField(), |
+ Add<HConstant>(String::kEmptyHashField)); |
+ Add<HStoreNamedField>(string, HObjectAccess::ForStringLength(), length); |
+ Add<HStoreNamedField>(string, HObjectAccess::ForConsStringFirst(), left); |
+ Add<HStoreNamedField>(string, HObjectAccess::ForConsStringSecond(), |
+ right); |
+ |
+ // Cons string is result. |
+ Push(string); |
+ } |
+ if_createcons.Else(); |
+ { |
+ // Check if both strings have the same encoding and both are |
+ // sequential. |
+ IfBuilder if_sameencodingandsequential(this); |
+ if_sameencodingandsequential.If<HCompareNumericAndBranch>( |
+ Add<HBitwise>( |
+ Token::BIT_AND, |
+ Add<HBitwise>( |
+ Token::BIT_XOR, left_instance_type, right_instance_type), |
+ Add<HConstant>(static_cast<int32_t>(kStringEncodingMask))), |
mvstanton
2013/11/08 10:17:50
More instances of your typical pattern here, OP1(O
|
+ graph()->GetConstant0(), Token::EQ); |
+ if_sameencodingandsequential.And(); |
+ if_sameencodingandsequential.If<HCompareNumericAndBranch>( |
+ Add<HBitwise>( |
+ Token::BIT_AND, |
+ Add<HBitwise>( |
+ Token::BIT_OR, left_instance_type, right_instance_type), |
+ Add<HConstant>(static_cast<int32_t>(kStringRepresentationMask))), |
+ Add<HConstant>(static_cast<int32_t>(kSeqStringTag)), Token::EQ); |
+ if_sameencodingandsequential.Then(); |
+ { |
+ // Check if the result is a one-byte string. |
+ IfBuilder if_onebyte(this); |
+ if_onebyte.If<HCompareNumericAndBranch>( |
+ Add<HBitwise>( |
+ Token::BIT_AND, |
+ Add<HBitwise>( |
+ Token::BIT_OR, left_instance_type, right_instance_type), |
+ Add<HConstant>(static_cast<int32_t>(kStringEncodingMask))), |
+ Add<HConstant>(static_cast<int32_t>(kTwoByteStringTag)), Token::NE); |
+ if_onebyte.Then(); |
+ { |
+ // Calculate the number of bytes needed for the characters in the |
+ // string while observing object alignment. |
+ STATIC_ASSERT(kOneByteSize == 1); |
+ STATIC_ASSERT((SeqOneByteString::kHeaderSize & |
+ kObjectAlignmentMask) == 0); |
+ HValue* size = Add<HAdd>( |
+ length, Add<HConstant>(static_cast<int32_t>( |
+ SeqOneByteString::kHeaderSize + kObjectAlignmentMask))); |
+ size->ClearFlag(HValue::kCanOverflow); |
+ size = Add<HBitwise>( |
+ Token::BIT_AND, size, Add<HConstant>(static_cast<int32_t>( |
+ ~kObjectAlignmentMask))); |
+ |
mvstanton
2013/11/08 10:17:50
The size operand calculation above is very tricky
Benedikt Meurer
2013/11/08 13:21:08
Done.
|
+ // Allocate the ASCII string object. |
+ Handle<Map> map = isolate()->factory()->ascii_string_map(); |
+ HAllocate* string = Add<HAllocate>(size, HType::String(), |
+ pretenure_flag, ASCII_STRING_TYPE); |
+ string->set_known_initial_map(map); |
+ |
+ // We can safely skip the write barrier for storing map here. |
+ HStoreNamedField* store_map = AddStoreMapConstant(string, map); |
mvstanton
2013/11/08 10:17:50
AddStoreMapConstantNoWriteBarrier() again, or as a
Benedikt Meurer
2013/11/08 13:21:08
Done.
|
+ store_map->SkipWriteBarrier(); |
+ |
+ // Copy bytes from the left string. |
+ BuildCopySeqStringChars( |
+ left, graph()->GetConstant0(), String::ONE_BYTE_ENCODING, |
+ string, graph()->GetConstant0(), String::ONE_BYTE_ENCODING, |
+ left_length); |
+ |
+ // Copy bytes from the right string. |
+ BuildCopySeqStringChars( |
+ right, graph()->GetConstant0(), String::ONE_BYTE_ENCODING, |
+ string, left_length, String::ONE_BYTE_ENCODING, |
+ right_length); |
+ |
+ // Return the string. |
+ Push(string); |
+ } |
+ if_onebyte.Else(); |
+ { |
+ // Calculate the number of bytes needed for the characters in the |
+ // string while observing object alignment. |
+ STATIC_ASSERT(kUC16Size == 2); |
+ STATIC_ASSERT((SeqTwoByteString::kHeaderSize & |
+ kObjectAlignmentMask) == 0); |
+ HValue* size = Add<HShl>(length, graph()->GetConstant1()); |
+ size->ClearFlag(HValue::kCanOverflow); |
+ size->SetFlag(HValue::kUint32); |
+ size = Add<HAdd>( |
+ size, Add<HConstant>(static_cast<int32_t>( |
+ SeqTwoByteString::kHeaderSize + kObjectAlignmentMask))); |
+ size->ClearFlag(HValue::kCanOverflow); |
+ size = Add<HBitwise>( |
+ Token::BIT_AND, size, Add<HConstant>(static_cast<int32_t>( |
+ ~kObjectAlignmentMask))); |
+ |
+ // Allocate the two-byte string object. |
+ Handle<Map> map = isolate()->factory()->string_map(); |
+ HAllocate* string = Add<HAllocate>(size, HType::String(), |
+ pretenure_flag, STRING_TYPE); |
+ string->set_known_initial_map(map); |
+ |
+ // We can safely skip the write barrier for storing map here. |
+ HStoreNamedField* store_map = AddStoreMapConstant(string, map); |
mvstanton
2013/11/08 10:17:50
Could you add a variant to AddStoreMapConstant eit
Benedikt Meurer
2013/11/08 13:21:08
Done.
|
+ store_map->SkipWriteBarrier(); |
+ |
+ // Copy bytes from the left string. |
+ BuildCopySeqStringChars( |
+ left, graph()->GetConstant0(), String::TWO_BYTE_ENCODING, |
+ string, graph()->GetConstant0(), String::TWO_BYTE_ENCODING, |
+ left_length); |
+ |
+ // Copy bytes from the right string. |
+ BuildCopySeqStringChars( |
+ right, graph()->GetConstant0(), String::TWO_BYTE_ENCODING, |
+ string, left_length, String::TWO_BYTE_ENCODING, |
+ right_length); |
+ |
+ // Return the string. |
+ Push(string); |
+ } |
+ if_onebyte.End(); |
+ |
+ // Initialize the (common) string fields. |
+ HValue* string = Pop(); |
+ Add<HStoreNamedField>(string, HObjectAccess::ForStringHashField(), |
+ Add<HConstant>(String::kEmptyHashField)); |
+ Add<HStoreNamedField>(string, HObjectAccess::ForStringLength(), |
+ length); |
+ Push(string); |
+ } |
+ if_sameencodingandsequential.JoinContinuation(&handled); |
+ } |
+ if_createcons.JoinContinuation(&handled); |
+ } |
+ if_nooverflow.JoinContinuation(&handled); |
+ |
+ // Check if the strings were concatenated successfully, otherwise fallback to |
+ // add the strings in the runtime. |
+ IfBuilder if_handled(this, &handled); |
+ if_handled.Then(); |
+ { |
+ // Count the native string addition. |
+ AddIncrementCounter(isolate()->counters()->string_add_native()); |
+ } |
+ if_handled.Else(); |
+ { |
+ // Fallback to the runtime to add the two strings. |
+ Add<HPushArgument>(left); |
+ Add<HPushArgument>(right); |
+ Push(Add<HCallRuntime>(isolate()->factory()->empty_string(), |
+ Runtime::FunctionForId(Runtime::kStringAdd), |
+ 2)); |
+ } |
+ if_handled.End(); |
+ |
+ return Pop(); |
+} |
+ |
+ |
+HValue* HGraphBuilder::BuildStringAdd(HValue* left, |
+ HValue* right, |
+ PretenureFlag pretenure_flag) { |
+ // Determine the string lengths. |
+ HValue* left_length = Add<HLoadNamedField>( |
+ left, HObjectAccess::ForStringLength()); |
+ HValue* right_length = Add<HLoadNamedField>( |
+ right, HObjectAccess::ForStringLength()); |
+ |
+ // Check if left string is empty. |
+ IfBuilder if_leftisempty(this); |
+ if_leftisempty.If<HCompareNumericAndBranch>( |
+ left_length, graph()->GetConstant0(), Token::EQ); |
+ if_leftisempty.Then(); |
+ { |
+ // Just return the right string. |
+ Push(right); |
+ } |
+ if_leftisempty.Else(); |
+ { |
+ // Check if right string is empty. |
+ IfBuilder if_rightisempty(this); |
+ if_rightisempty.If<HCompareNumericAndBranch>( |
+ right_length, graph()->GetConstant0(), Token::EQ); |
+ if_rightisempty.Then(); |
+ { |
+ // Just return the left string. |
+ Push(left); |
+ } |
+ if_rightisempty.Else(); |
+ { |
+ // Concatenate the two non-empty strings. |
+ Push(BuildUncheckedStringAdd(left, right, pretenure_flag)); |
+ } |
+ if_rightisempty.End(); |
+ } |
+ if_leftisempty.End(); |
+ |
+ return Pop(); |
+} |
+ |
+ |
HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess( |
HValue* checked_object, |
HValue* key, |