Index: src/compiler/js-typed-lowering.cc |
diff --git a/src/compiler/js-typed-lowering.cc b/src/compiler/js-typed-lowering.cc |
index 32d4fe6cba17192adb8bbc78785e6d85ef404f29..070d3cf818de5099dfa8ccb32991b7756dc8e560 100644 |
--- a/src/compiler/js-typed-lowering.cc |
+++ b/src/compiler/js-typed-lowering.cc |
@@ -46,6 +46,7 @@ class JSBinopReduction final { |
return true; |
case BinaryOperationHint::kAny: |
case BinaryOperationHint::kNone: |
+ case BinaryOperationHint::kString: |
break; |
} |
} |
@@ -73,6 +74,30 @@ class JSBinopReduction final { |
return false; |
} |
+ // Check if a string addition will definitely result in creating a ConsString, |
+ // i.e. if the combined length of the resulting string exceeds the ConsString |
+ // minimum length. |
+ bool ShouldCreateConsString() { |
+ DCHECK_EQ(IrOpcode::kJSAdd, node_->opcode()); |
+ if (BothInputsAre(Type::String()) || |
+ ((lowering_->flags() & JSTypedLowering::kDeoptimizationEnabled) && |
+ BinaryOperationHintOf(node_->op()) == BinaryOperationHint::kString)) { |
+ if (left_type()->IsConstant() && |
+ left_type()->AsConstant()->Value()->IsString()) { |
+ Handle<String> left_string = |
+ Handle<String>::cast(left_type()->AsConstant()->Value()); |
+ if (left_string->length() >= ConsString::kMinLength) return true; |
+ } |
+ if (right_type()->IsConstant() && |
+ right_type()->AsConstant()->Value()->IsString()) { |
+ Handle<String> right_string = |
+ Handle<String>::cast(right_type()->AsConstant()->Value()); |
+ if (right_string->length() >= ConsString::kMinLength) return true; |
+ } |
+ } |
+ return false; |
+ } |
+ |
void ConvertInputsToNumber() { |
// To convert the inputs to numbers, we have to provide frame states |
// for lazy bailouts in the ToNumber conversions. |
@@ -467,6 +492,9 @@ Reduction JSTypedLowering::ReduceJSAdd(Node* node) { |
return r.ChangeToPureOperator(simplified()->NumberAdd(), Type::Number()); |
} |
if (r.OneInputIs(Type::String())) { |
+ if (r.ShouldCreateConsString()) { |
+ return ReduceCreateConsString(node); |
+ } |
StringAddFlags flags = STRING_ADD_CHECK_NONE; |
if (!r.LeftInputIs(Type::String())) { |
flags = STRING_ADD_CONVERT_LEFT; |
@@ -544,6 +572,123 @@ Reduction JSTypedLowering::ReduceUI32Shift(Node* node, Signedness signedness) { |
return NoChange(); |
} |
+Reduction JSTypedLowering::ReduceCreateConsString(Node* node) { |
+ Node* first = NodeProperties::GetValueInput(node, 0); |
+ Node* second = NodeProperties::GetValueInput(node, 1); |
+ Node* context = NodeProperties::GetContextInput(node); |
+ Node* frame_state = NodeProperties::GetFrameStateInput(node); |
+ Node* effect = NodeProperties::GetEffectInput(node); |
+ Node* control = NodeProperties::GetControlInput(node); |
+ |
+ // Make sure {first} is actually a String. |
+ Type* first_type = NodeProperties::GetType(first); |
+ if (!first_type->Is(Type::String())) { |
+ first = effect = |
+ graph()->NewNode(simplified()->CheckString(), first, effect, control); |
+ first_type = NodeProperties::GetType(first); |
+ } |
+ |
+ // Make sure {second} is actually a String. |
+ Type* second_type = NodeProperties::GetType(second); |
+ if (!second_type->Is(Type::String())) { |
+ second = effect = |
+ graph()->NewNode(simplified()->CheckString(), second, effect, control); |
+ second_type = NodeProperties::GetType(second); |
+ } |
+ |
+ // Determine the {first} length. |
+ Node* first_length = |
+ first_type->IsConstant() |
+ ? jsgraph()->Constant( |
+ Handle<String>::cast(first_type->AsConstant()->Value()) |
+ ->length()) |
+ : effect = graph()->NewNode( |
+ simplified()->LoadField(AccessBuilder::ForStringLength()), |
+ first, effect, control); |
+ |
+ // Determine the {second} length. |
+ Node* second_length = |
+ second_type->IsConstant() |
+ ? jsgraph()->Constant( |
+ Handle<String>::cast(second_type->AsConstant()->Value()) |
+ ->length()) |
+ : effect = graph()->NewNode( |
+ simplified()->LoadField(AccessBuilder::ForStringLength()), |
+ second, effect, control); |
+ |
+ // Compute the resulting length. |
+ Node* length = |
+ graph()->NewNode(simplified()->NumberAdd(), first_length, second_length); |
+ |
+ // Check if we would overflow the allowed maximum string length. |
+ Node* check = graph()->NewNode(simplified()->NumberLessThanOrEqual(), length, |
+ jsgraph()->Constant(String::kMaxLength)); |
+ Node* branch = |
+ graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); |
+ Node* if_false = graph()->NewNode(common()->IfFalse(), branch); |
+ Node* efalse = effect; |
+ { |
+ // Throw a RangeError in case of overflow. |
+ Node* vfalse = efalse = graph()->NewNode( |
+ javascript()->CallRuntime(Runtime::kThrowInvalidStringLength), context, |
+ frame_state, efalse, if_false); |
+ if_false = graph()->NewNode(common()->IfSuccess(), vfalse); |
+ if_false = graph()->NewNode(common()->Throw(), vfalse, efalse, if_false); |
+ // TODO(bmeurer): This should be on the AdvancedReducer somehow. |
+ NodeProperties::MergeControlToEnd(graph(), common(), if_false); |
+ Revisit(graph()->end()); |
+ |
+ // Update potential {IfException} uses of {node} to point to the |
+ // %ThrowInvalidStringLength runtime call node instead. |
+ for (Edge edge : node->use_edges()) { |
+ if (edge.from()->opcode() == IrOpcode::kIfException) { |
+ DCHECK(NodeProperties::IsControlEdge(edge) || |
+ NodeProperties::IsEffectEdge(edge)); |
+ edge.UpdateTo(vfalse); |
+ Revisit(edge.from()); |
+ } |
+ } |
+ } |
+ control = graph()->NewNode(common()->IfTrue(), branch); |
+ |
+ // Figure out the map for the resulting ConsString. |
+ // TODO(turbofan): We currently just use the cons_string_map here for |
+ // the sake of simplicity; we could also try to be smarter here and |
+ // use the one_byte_cons_string_map instead when the resulting ConsString |
+ // contains only one byte characters. |
+ Node* value_map = jsgraph()->HeapConstant(factory()->cons_string_map()); |
+ |
+ // Allocate the resulting ConsString. |
+ effect = graph()->NewNode( |
+ common()->BeginRegion(RegionObservability::kNotObservable), effect); |
+ Node* value = effect = |
+ graph()->NewNode(simplified()->Allocate(NOT_TENURED), |
+ jsgraph()->Constant(ConsString::kSize), effect, control); |
+ NodeProperties::SetType(value, Type::OtherString()); |
+ effect = graph()->NewNode(simplified()->StoreField(AccessBuilder::ForMap()), |
+ value, value_map, effect, control); |
+ effect = graph()->NewNode( |
+ simplified()->StoreField(AccessBuilder::ForNameHashField()), value, |
+ jsgraph()->Uint32Constant(Name::kEmptyHashField), effect, control); |
+ effect = graph()->NewNode( |
+ simplified()->StoreField(AccessBuilder::ForStringLength()), value, length, |
+ effect, control); |
+ effect = graph()->NewNode( |
+ simplified()->StoreField(AccessBuilder::ForConsStringFirst()), value, |
+ first, effect, control); |
+ effect = graph()->NewNode( |
+ simplified()->StoreField(AccessBuilder::ForConsStringSecond()), value, |
+ second, effect, control); |
+ |
+ // Morph the {node} into a {FinishRegion}. |
+ ReplaceWithValue(node, node, node, control); |
+ node->ReplaceInput(0, value); |
+ node->ReplaceInput(1, effect); |
+ node->TrimInputCount(2); |
+ NodeProperties::ChangeOp(node, common()->FinishRegion()); |
+ return Changed(node); |
+} |
+ |
Reduction JSTypedLowering::ReduceJSComparison(Node* node) { |
JSBinopReduction r(this, node); |
if (r.BothInputsAre(Type::String())) { |