| Index: src/hydrogen.cc
|
| diff --git a/src/hydrogen.cc b/src/hydrogen.cc
|
| index 147563e90104c7d094c93b937bb91c0f34534fc6..c09cd6df762b0f9cd50027b19fe63749f4692714 100644
|
| --- a/src/hydrogen.cc
|
| +++ b/src/hydrogen.cc
|
| @@ -1469,6 +1469,342 @@ HValue* HGraphBuilder::BuildNumberToString(HValue* object,
|
| }
|
|
|
|
|
| +HValue* HGraphBuilder::BuildSeqStringSizeFor(HValue* length,
|
| + String::Encoding encoding) {
|
| + STATIC_ASSERT((SeqString::kHeaderSize & kObjectAlignmentMask) == 0);
|
| + HValue* size = length;
|
| + if (encoding == String::TWO_BYTE_ENCODING) {
|
| + size = Add<HShl>(length, graph()->GetConstant1());
|
| + size->ClearFlag(HValue::kCanOverflow);
|
| + size->SetFlag(HValue::kUint32);
|
| + }
|
| + size = Add<HAdd>(size, Add<HConstant>(static_cast<int32_t>(
|
| + SeqString::kHeaderSize + kObjectAlignmentMask)));
|
| + size->ClearFlag(HValue::kCanOverflow);
|
| + size = Add<HBitwise>(
|
| + Token::BIT_AND, size, Add<HConstant>(static_cast<int32_t>(
|
| + ~kObjectAlignmentMask)));
|
| + return size;
|
| +}
|
| +
|
| +
|
| +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. Unfortunately
|
| + // we cannot actually check whether the combined length of both strings
|
| + // exceeds String::kMaxLength (because of unclear results from the
|
| + // representation inference phase), so we use a pessimistic approach here
|
| + // instead, checking that the length of either substring does not exceed half
|
| + // of String::kMaxLength.
|
| + HConstant* max_length = Add<HConstant>(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 difference of instance types.
|
| + HValue* xored_instance_types = Add<HBitwise>(
|
| + Token::BIT_XOR, left_instance_type, right_instance_type);
|
| +
|
| + // 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>(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);
|
| +
|
| + // Compute the intersection of instance types.
|
| + HValue* anded_instance_types = Add<HBitwise>(
|
| + Token::BIT_AND, left_instance_type, right_instance_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 if the one-byte data hint is set in
|
| + // both strings, or
|
| + // 2. 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(kOneByteStringTag != 0);
|
| + STATIC_ASSERT(kOneByteDataHintMask != 0);
|
| + if_onebyte.If<HCompareNumericAndBranch>(
|
| + Add<HBitwise>(
|
| + Token::BIT_AND, anded_instance_types,
|
| + Add<HConstant>(static_cast<int32_t>(
|
| + kStringEncodingMask | kOneByteDataHintMask))),
|
| + graph()->GetConstant0(), Token::NE);
|
| + if_onebyte.Or();
|
| + STATIC_ASSERT(kOneByteStringTag != 0 &&
|
| + kOneByteDataHintTag != 0 &&
|
| + kOneByteDataHintTag != kOneByteStringTag);
|
| + if_onebyte.If<HCompareNumericAndBranch>(
|
| + Add<HBitwise>(
|
| + Token::BIT_AND, xored_instance_types,
|
| + 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.
|
| + Handle<Map> map = isolate()->factory()->cons_ascii_string_map();
|
| + AddStoreMapConstantNoWriteBarrier(string, map);
|
| + }
|
| + if_onebyte.Else();
|
| + {
|
| + // We can safely skip the write barrier for storing the map here.
|
| + Handle<Map> map = isolate()->factory()->cons_string_map();
|
| + AddStoreMapConstantNoWriteBarrier(string, map);
|
| + }
|
| + 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();
|
| + {
|
| + // Compute union of instance types.
|
| + HValue* ored_instance_types = Add<HBitwise>(
|
| + Token::BIT_OR, left_instance_type, right_instance_type);
|
| +
|
| + // 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, xored_instance_types,
|
| + Add<HConstant>(static_cast<int32_t>(kStringEncodingMask))),
|
| + graph()->GetConstant0(), Token::EQ);
|
| + if_sameencodingandsequential.And();
|
| + STATIC_ASSERT(kSeqStringTag == 0);
|
| + if_sameencodingandsequential.If<HCompareNumericAndBranch>(
|
| + Add<HBitwise>(
|
| + Token::BIT_AND, ored_instance_types,
|
| + Add<HConstant>(static_cast<int32_t>(kStringRepresentationMask))),
|
| + graph()->GetConstant0(), Token::EQ);
|
| + if_sameencodingandsequential.Then();
|
| + {
|
| + // Check if the result is a one-byte string.
|
| + IfBuilder if_onebyte(this);
|
| + STATIC_ASSERT(kOneByteStringTag != 0);
|
| + if_onebyte.If<HCompareNumericAndBranch>(
|
| + Add<HBitwise>(
|
| + Token::BIT_AND, ored_instance_types,
|
| + Add<HConstant>(static_cast<int32_t>(kStringEncodingMask))),
|
| + graph()->GetConstant0(), Token::NE);
|
| + if_onebyte.Then();
|
| + {
|
| + // Calculate the number of bytes needed for the characters in the
|
| + // string while observing object alignment.
|
| + HValue* size = BuildSeqStringSizeFor(
|
| + length, String::ONE_BYTE_ENCODING);
|
| +
|
| + // 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.
|
| + AddStoreMapConstantNoWriteBarrier(string, map);
|
| +
|
| + // 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.
|
| + HValue* size = BuildSeqStringSizeFor(
|
| + length, String::TWO_BYTE_ENCODING);
|
| +
|
| + // 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.
|
| + AddStoreMapConstantNoWriteBarrier(string, map);
|
| +
|
| + // 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();
|
| + {
|
| + // Count the native string addition.
|
| + AddIncrementCounter(isolate()->counters()->string_add_native());
|
| +
|
| + // 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();
|
| + {
|
| + // Count the native string addition.
|
| + AddIncrementCounter(isolate()->counters()->string_add_native());
|
| +
|
| + // 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,
|
|
|