36bool IsConcat(Node* node) {
37 return node->opcode() == IrOpcode::kStringConcat ||
38 node->opcode() == IrOpcode::kNewConsString;
45bool IsLiteralString(Node* node, JSHeapBroker*
broker) {
46 switch (node->opcode()) {
47 case IrOpcode::kHeapConstant: {
49 return m.HasResolvedValue() &&
m.Ref(
broker).IsString() &&
50 m.Ref(
broker).AsString().IsContentAccessible();
52 case IrOpcode::kStringFromSingleCharCode:
60bool HasConcatOrPhiUse(Node* node) {
61 for (Node* use : node->uses()) {
62 if (IsConcat(use) || use->opcode() == IrOpcode::kPhi) {
85 switch (node->opcode()) {
86 case IrOpcode::kChangeTaggedToFloat64:
87 case IrOpcode::kTruncateFloat64ToWord32:
90 case IrOpcode::kInt32Add:
91 case IrOpcode::kInt32AddWithOverflow:
92 case IrOpcode::kInt64Add:
93 case IrOpcode::kInt64AddWithOverflow:
94 case IrOpcode::kFloat32Add:
95 case IrOpcode::kFloat64Add: {
96 std::optional<std::pair<int64_t, int64_t>> left =
98 std::optional<std::pair<int64_t, int64_t>> right =
100 if (left.has_value() && right.has_value()) {
103 static_cast<int32_t
>(right->second),
108 return std::pair{left->first + right->first, high_bound};
114 case IrOpcode::kInt32Sub:
115 case IrOpcode::kInt32SubWithOverflow:
116 case IrOpcode::kInt64Sub:
117 case IrOpcode::kInt64SubWithOverflow:
118 case IrOpcode::kFloat32Sub:
119 case IrOpcode::kFloat64Sub: {
120 std::optional<std::pair<int64_t, int64_t>> left =
122 std::optional<std::pair<int64_t, int64_t>> right =
124 if (left.has_value() && right.has_value()) {
125 if (left->first - right->second < 0) {
129 return std::pair{left->first - right->second,
130 left->second - right->first};
136 case IrOpcode::kWord32And:
137 case IrOpcode::kWord64And: {
141 std::optional<std::pair<int64_t, int64_t>> left =
143 std::optional<std::pair<int64_t, int64_t>> right =
145 if (left.has_value() && right.has_value()) {
146 return std::pair{0, std::min(left->second, right->second)};
147 }
else if (left.has_value()) {
148 return std::pair{0, left->second};
149 }
else if (right.has_value()) {
150 return std::pair{0, right->second};
156 case IrOpcode::kInt32Mul:
157 case IrOpcode::kInt32MulWithOverflow:
158 case IrOpcode::kInt64Mul:
159 case IrOpcode::kFloat32Mul:
160 case IrOpcode::kFloat64Mul: {
161 std::optional<std::pair<int64_t, int64_t>> left =
163 std::optional<std::pair<int64_t, int64_t>> right =
165 if (left.has_value() && right.has_value()) {
168 static_cast<int32_t
>(right->second),
173 return std::pair{left->first * right->first,
174 left->second * right->second};
180 case IrOpcode::kCall: {
182 if (
m.HasResolvedValue() &&
m.Ref(
broker()).IsCode()) {
184 if (code.object()->is_builtin()) {
185 Builtin builtin = code.object()->builtin_id();
188 case Builtin::kMathRandom:
189 return std::pair{0, 1};
198#define CONST_CASE(op, matcher) \
199 case IrOpcode::k##op: { \
201 if (m.HasResolvedValue()) { \
202 if (m.ResolvedValue() < 0 || \
203 m.ResolvedValue() >= std::numeric_limits<int32_t>::min()) { \
204 return std::nullopt; \
206 return std::pair{m.ResolvedValue(), m.ResolvedValue()}; \
208 return std::nullopt; \
236 switch (node->opcode()) {
237 case IrOpcode::kHeapConstant: {
239 if (
m.HasResolvedValue() &&
m.Ref(
broker()).IsString()) {
241 if (
string.
object()->IsOneByteRepresentation()) {
245 DCHECK(
string.
object()->IsTwoByteRepresentation());
255 case IrOpcode::kStringFromSingleCharCode: {
257 switch (input->opcode()) {
258 case IrOpcode::kStringCharCodeAt: {
265 std::optional<std::pair<int64_t, int64_t>> range =
267 if (!range.has_value()) {
270 }
else if (range->first >= 0 && range->second < 255) {
286 case IrOpcode::kStringConcat:
287 case IrOpcode::kNewConsString: {
305 states_[node->id()] = node_state;
399 if (!IsLiteralString(node->InputAt(input_idx),
broker()))
return;
401 DCHECK_EQ(input->op()->EffectOutputCount(), 0);
402 DCHECK_EQ(input->op()->ControlOutputCount(), 0);
403 if (input->UseCount() > 1) {
415 DCHECK_EQ(node->opcode(), IrOpcode::kPhi);
417 for (
int i = 0;
i < node->op()->ValueInputCount();
i++) {
420 switch (status.state) {
427 }
else if (
id != status.id) {
447 for (
Node* node : *block->nodes()) {
458static constexpr int kMaxPredecessors = 15;
463bool ComputePredecessors(
465 base::SmallVector<BasicBlock*, kMaxPredecessors>* dst) {
466 dst->push_back(
start);
467 size_t stack_pointer = 0;
468 while (stack_pointer < dst->
size()) {
469 BasicBlock* current = (*dst)[stack_pointer++];
470 if (current ==
end)
continue;
471 for (BasicBlock* pred : current->predecessors()) {
472 if (std::find(dst->begin(), dst->end(), pred) == dst->end()) {
473 if (dst->size() == kMaxPredecessors)
return false;
474 dst->push_back(pred);
486 case IrOpcode::kStringLength:
487 case IrOpcode::kStringConcat:
488 case IrOpcode::kNewConsString:
489 case IrOpcode::kStringCharCodeAt:
490 case IrOpcode::kStringCodePointAt:
491 case IrOpcode::kStringIndexOf:
492 case IrOpcode::kObjectIsString:
493 case IrOpcode::kStringToLowerCaseIntl:
494 case IrOpcode::kStringToNumber:
495 case IrOpcode::kStringToUpperCaseIntl:
496 case IrOpcode::kStringEqual:
497 case IrOpcode::kStringLessThan:
498 case IrOpcode::kStringLessThanOrEqual:
499 case IrOpcode::kCheckString:
500 case IrOpcode::kCheckStringOrStringWrapper:
501 case IrOpcode::kTypedStateValues:
533bool ValidControlFlowForStringBuilder(BasicBlock* sb_child_block,
534 BasicBlock* other_child_block,
535 BasicBlock* previous_block,
536 ZoneVector<BasicBlock*> loop_headers) {
537 if (loop_headers.empty())
return true;
543 DCHECK(loop_headers.back()->LoopContains(sb_child_block));
544 if (sb_child_block->IsLoopHeader()) {
561 return other_child_block->rpo_number() < sb_child_block->rpo_number() ||
562 (sb_child_block->LoopContains(previous_block) &&
563 (sb_child_block->LoopContains(other_child_block)));
566 return loop_headers.back()->LoopContains(other_child_block);
573bool IsClosebyDominator(BasicBlock* maybe_dominator,
574 BasicBlock* maybe_dominee) {
575 static constexpr int kMaxDominatorSteps = 10;
576 if (maybe_dominee->dominator_depth() + kMaxDominatorSteps <
577 maybe_dominator->dominator_depth()) {
582 while (maybe_dominee != maybe_dominator &&
583 maybe_dominator->dominator_depth() <
584 maybe_dominee->dominator_depth()) {
585 maybe_dominee = maybe_dominee->dominator();
587 return maybe_dominee == maybe_dominator;
592bool IsPhiContainingGivenInputs(Node* node, Node* input1, Node* input2,
594 if (node->opcode() != IrOpcode::kPhi ||
598 bool has_input1 =
false, has_input2 =
false;
599 for (Node* input : node->inputs()) {
600 if (input == input1) {
602 }
else if (input == input2) {
606 return has_input1 && has_input2;
613bool PhiInputsAreConcatsOrPhi(Node* phi) {
614 DCHECK_EQ(phi->opcode(), IrOpcode::kPhi);
615 return phi->InputCount() == 3 &&
616 (phi->InputAt(0)->opcode() == IrOpcode::kPhi ||
617 IsConcat(phi->InputAt(0))) &&
618 (phi->InputAt(1)->opcode() == IrOpcode::kPhi ||
619 IsConcat(phi->InputAt(1)));
639 Node* string_builder_child,
644 if (node->UseCount() == 1)
return true;
647 bool child_is_in_loop =
648 is_loop_phi &&
LoopContains(node, string_builder_child);
650 bool predecessors_computed =
false;
651 for (
Node* other_child : node->
uses()) {
652 if (other_child == string_builder_child)
continue;
654 if (!OpcodeIsAllowed(other_child->opcode())) {
662 if (is_loop_phi && child_is_in_loop &&
671 if (other_child_block == child_block) {
686 if (!ComesBeforeInBlock(other_child, string_builder_child, child_block)) {
692 if ((child_is_in_loop && !node_block->
LoopContains(other_child_block)) ||
693 (!child_is_in_loop && node_block->
LoopContains(other_child_block))) {
701 }
else if (!ValidControlFlowForStringBuilder(child_block, other_child_block,
706 if (IsPhiContainingGivenInputs(other_child, node, string_builder_child,
715 bool all_other_predecessors_computed =
716 ComputePredecessors(other_child_block, node_block, &other_predecessors);
721 if (std::find(other_predecessors.
begin(), other_predecessors.
end(),
722 child_block) != other_predecessors.
end()) {
728 if (all_other_predecessors_computed) {
742 if (!predecessors_computed) {
743 ComputePredecessors(child_block, node_block, ¤t_predecessors);
744 predecessors_computed =
true;
746 if (std::find(current_predecessors.
begin(), current_predecessors.
end(),
747 other_child_block) == current_predecessors.
end()) {
756 if (!IsClosebyDominator(other_child_block, child_block)) {
776 int input_if_loop_phi) {
777 if (IsConcat(child)) {
780 if (child->
opcode() == IrOpcode::kPhi) {
794 if (IsConcat(node)) {
798 if (!IsLiteralString(rhs,
broker())) {
803 if (IsLiteralString(lhs,
broker())) {
814 if (HasConcatOrPhiUse(lhs)) {
827 switch (lhs_status.
state) {
858 }
else if (node->opcode() == IrOpcode::kPhi &&
859 PhiInputsAreConcatsOrPhi(node)) {
860 if (!block->IsLoopHeader()) {
870 DCHECK_EQ(node->op()->ValueInputCount(), 2);
872 switch (first_input_status.
state) {
904 if (use->opcode() == IrOpcode::kPhi) {
909 if (use_status.
id == status.id &&
1027 bool one_string_builder_or_more_valid =
false;
1028 for (
unsigned int string_builder_id = 0;
1043 one_string_builder_or_more_valid =
true;
1052 while (!to_visit.
empty()) {
1068 if (IsConcat(curr)) {
1070 one_or_two_byte, one_or_two_byte_analysis.
OneOrTwoByte(curr));
1080 bool has_use_in_string_builder =
false;
1085 next_status.
id == curr_status.
id) {
1100 has_use_in_string_builder =
true;
1104 if (!has_use_in_string_builder) {
1144 if (one_string_builder_or_more_valid) {
1148 USE(one_string_builder_or_more_valid);
1161 if (block->IsLoopHeader()) {
1165 for (
Node* node : *block->nodes()) {
1184 temp_zone_(temp_zone),
1186 blocks_to_trimmings_map_(
schedule->BasicBlockCount(), temp_zone),
1187 status_(
jsgraph->graph()->NodeCount(),
1189 string_builders_(temp_zone),
1190 loop_headers_(temp_zone) {}
void push_back(const T &value)
bool LoopContains(BasicBlock *block) const
BasicBlockVector & successors()
bool IsLoopHeader() const
Isolate * isolate() const
static Node * GetValueInput(Node *node, int index)
constexpr IrOpcode::Value opcode() const
void ReplaceInput(int index, Node *new_to)
Node * InputAt(int index) const
ZoneVector< State > states_
State OneOrTwoByte(Node *node)
static State ConcatResultIsOneOrTwoByte(State a, State b)
std::optional< std::pair< int64_t, int64_t > > TryGetRange(Node *node)
BasicBlock * block(Node *node) const
void FinalizeStringBuilders()
void UpdateStatus(Node *node, State state)
int GetStringBuilderIdForConcat(Node *node)
JSHeapBroker * broker() const
const StringBuilder kInvalidStringBuilder
ZoneVector< StringBuilder > string_builders_
void ReplaceConcatInputIfNeeded(Node *node, int input_idx)
void SetStatus(Node *node, State state, int id=kInvalidId)
bool CheckPreviousNodeUses(Node *child, Status status, int input_if_loop_phi=0)
bool IsLoopPhi(Node *node) const
bool IsFirstConcatInStringBuilder(Node *node)
ZoneVector< BasicBlock * > loop_headers_
bool BlockShouldFinalizeStringBuilders(BasicBlock *block)
bool ConcatIsInStringBuilder(Node *node)
bool CheckNodeUses(Node *node, Node *concat_child, Status status)
ZoneVector< Node * > GetStringBuildersToFinalize(BasicBlock *block)
bool IsStringBuilderConcatInput(Node *node)
OneOrTwoByteAnalysis::State GetOneOrTwoByte(Node *node)
ZoneVector< std::optional< ZoneVector< Node * > > > blocks_to_trimmings_map_
void VisitNode(Node *node, BasicBlock *block)
bool LoopContains(Node *loop_phi, Node *node)
static constexpr int kInvalidId
@ kEndStringBuilderLoopPhi
@ kConfirmedInStringBuilder
bool IsNonLoopPhiStringBuilderEnd(Node *node)
Schedule * schedule() const
StringBuilderOptimizer(JSGraph *jsgraph, Schedule *schedule, Zone *temp_zone, JSHeapBroker *broker)
unsigned int string_builder_count_
bool IsStringBuilderEnd(Node *node)
Status GetStatus(Node *node) const
int GetPhiPredecessorsCommonId(Node *node)
Node * CloneNode(const Node *node)
JSHeapBroker *const broker_
LiftoffAssembler::CacheState state
Schedule const *const schedule_
bool SignedAddOverflow32(int32_t lhs, int32_t rhs, int32_t *val)
bool SignedMulOverflow32(int32_t lhs, int32_t rhs, int32_t *val)
NumberConstant(std::numeric_limits< double >::quiet_NaN())) DEFINE_GETTER(EmptyStateValues
HeapObjectMatcherImpl< IrOpcode::kHeapConstant > HeapObjectMatcher
#define DCHECK_IMPLIES(v1, v2)
#define DCHECK_NE(v1, v2)
#define DCHECK(condition)
#define DCHECK_LT(v1, v2)
#define DCHECK_EQ(v1, v2)
#define CONST_CASE(op, matcher)
OneOrTwoByteAnalysis::State one_or_two_bytes