Chromium Code Reviews| 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, |