| Index: src/hydrogen.cc
|
| diff --git a/src/hydrogen.cc b/src/hydrogen.cc
|
| index fd93ce3296dd5e75cbf1e733a41b3bdf9903720b..8bb7f915e426fbd9fd043eca09c31f94039711b4 100644
|
| --- a/src/hydrogen.cc
|
| +++ b/src/hydrogen.cc
|
| @@ -1733,248 +1733,237 @@ HValue* HGraphBuilder::BuildUncheckedStringAdd(HValue* left,
|
| 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();
|
| + // Compute the combined string length. If the result is larger than the max
|
| + // supported string length, we bailout to the runtime. This is done implicitly
|
| + // when converting the result back to a smi in case the max string length
|
| + // equals the max smi valie. Otherwise, for platforms with 32-bit smis, we do
|
| + HValue* length = AddUncasted<HAdd>(left_length, right_length);
|
| + STATIC_ASSERT(String::kMaxLength <= Smi::kMaxValue);
|
| + if (String::kMaxLength != Smi::kMaxValue) {
|
| + IfBuilder if_nooverflow(this);
|
| + if_nooverflow.If<HCompareNumericAndBranch>(
|
| + length, Add<HConstant>(String::kMaxLength), Token::LTE);
|
| + if_nooverflow.Then();
|
| + if_nooverflow.ElseDeopt("String length exceeds limit");
|
| + }
|
| +
|
| + // 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 = AddUncasted<HBitwise>(
|
| + Token::BIT_XOR, left_instance_type, right_instance_type);
|
| +
|
| + // 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();
|
| {
|
| - // 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 = AddUncasted<HBitwise>(
|
| - Token::BIT_XOR, left_instance_type, right_instance_type);
|
| -
|
| - // Compute the length of the resulting string.
|
| - HValue* length = AddUncasted<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 = AddUncasted<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>(
|
| + AddUncasted<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>(
|
| + AddUncasted<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();
|
| {
|
| - // 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 = AddUncasted<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.
|
| + // 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);
|
| +
|
| + // Count the native string addition.
|
| + AddIncrementCounter(isolate()->counters()->string_add_native());
|
| +
|
| + // Cons string is result.
|
| + Push(string);
|
| + }
|
| + if_createcons.Else();
|
| + {
|
| + // Compute union of instance types.
|
| + HValue* ored_instance_types = AddUncasted<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>(
|
| + AddUncasted<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>(
|
| + AddUncasted<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);
|
| - STATIC_ASSERT(kOneByteDataHintMask != 0);
|
| if_onebyte.If<HCompareNumericAndBranch>(
|
| AddUncasted<HBitwise>(
|
| - Token::BIT_AND, anded_instance_types,
|
| - Add<HConstant>(static_cast<int32_t>(
|
| - kStringEncodingMask | kOneByteDataHintMask))),
|
| + Token::BIT_AND, ored_instance_types,
|
| + Add<HConstant>(static_cast<int32_t>(kStringEncodingMask))),
|
| graph()->GetConstant0(), Token::NE);
|
| - if_onebyte.Or();
|
| - STATIC_ASSERT(kOneByteStringTag != 0 &&
|
| - kOneByteDataHintTag != 0 &&
|
| - kOneByteDataHintTag != kOneByteStringTag);
|
| - if_onebyte.If<HCompareNumericAndBranch>(
|
| - AddUncasted<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();
|
| + // 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);
|
| +
|
| + // Length must be stored into the string before we copy characters to
|
| + // make debug verification code happy.
|
| + Add<HStoreNamedField>(string, HObjectAccess::ForStringLength(),
|
| + length);
|
| +
|
| + // 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);
|
| +
|
| + // Count the native string addition.
|
| + AddIncrementCounter(isolate()->counters()->string_add_native());
|
| +
|
| + // Return the string.
|
| + Push(string);
|
| }
|
| if_onebyte.Else();
|
| {
|
| - // We can safely skip the write barrier for storing the map here.
|
| - Handle<Map> map = isolate()->factory()->cons_string_map();
|
| + // 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);
|
| +
|
| + // Length must be stored into the string before we copy characters to
|
| + // make debug verification code happy.
|
| + Add<HStoreNamedField>(string, HObjectAccess::ForStringLength(),
|
| + length);
|
| +
|
| + // 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 cons string fields.
|
| + // Initialize the (common) string fields.
|
| + HValue* string = Pop();
|
| 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.
|
| + // Count the native string addition.
|
| + AddIncrementCounter(isolate()->counters()->string_add_native());
|
| +
|
| Push(string);
|
| }
|
| - if_createcons.Else();
|
| + if_sameencodingandsequential.Else();
|
| {
|
| - // Compute union of instance types.
|
| - HValue* ored_instance_types = AddUncasted<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>(
|
| - AddUncasted<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>(
|
| - AddUncasted<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>(
|
| - AddUncasted<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);
|
| -
|
| - // Length must be stored into the string before we copy characters to
|
| - // make debug verification code happy.
|
| - Add<HStoreNamedField>(string, HObjectAccess::ForStringLength(),
|
| - length);
|
| -
|
| - // 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);
|
| -
|
| - // Length must be stored into the string before we copy characters to
|
| - // make debug verification code happy.
|
| - Add<HStoreNamedField>(string, HObjectAccess::ForStringLength(),
|
| - length);
|
| -
|
| - // 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));
|
| - Push(string);
|
| - }
|
| - if_sameencodingandsequential.JoinContinuation(&handled);
|
| + // 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_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_sameencodingandsequential.End();
|
| }
|
| - if_handled.End();
|
| + if_createcons.End();
|
|
|
| return Pop();
|
| }
|
|
|