diff --git a/src/compiler/effect-control-linearizer.cc b/src/compiler/effect-control-linearizer.cc index 3ab0d781bc..7f63759a38 100644 --- a/src/compiler/effect-control-linearizer.cc +++ b/src/compiler/effect-control-linearizer.cc @@ -860,6 +860,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node, case IrOpcode::kStringIndexOf: result = LowerStringIndexOf(node); break; + case IrOpcode::kStringLength: + result = LowerStringLength(node); + break; case IrOpcode::kStringToNumber: result = LowerStringToNumber(node); break; @@ -2843,6 +2846,12 @@ Node* EffectControlLinearizer::LowerStringIndexOf(Node* node) { position, __ NoContextConstant()); } +Node* EffectControlLinearizer::LowerStringLength(Node* node) { + Node* subject = node->InputAt(0); + + return __ LoadField(AccessBuilder::ForStringLength(), subject); +} + Node* EffectControlLinearizer::LowerStringComparison(Callable const& callable, Node* node) { Node* lhs = node->InputAt(0); diff --git a/src/compiler/effect-control-linearizer.h b/src/compiler/effect-control-linearizer.h index 76b56af71c..ffcf32d9d4 100644 --- a/src/compiler/effect-control-linearizer.h +++ b/src/compiler/effect-control-linearizer.h @@ -117,6 +117,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer { Node* LowerStringFromCharCode(Node* node); Node* LowerStringFromCodePoint(Node* node); Node* LowerStringIndexOf(Node* node); + Node* LowerStringLength(Node* node); Node* LowerStringEqual(Node* node); Node* LowerStringLessThan(Node* node); Node* LowerStringLessThanOrEqual(Node* node); diff --git a/src/compiler/js-builtin-reducer.cc b/src/compiler/js-builtin-reducer.cc index 8f89103a8d..cb56f0253b 100644 --- a/src/compiler/js-builtin-reducer.cc +++ b/src/compiler/js-builtin-reducer.cc @@ -2391,9 +2391,8 @@ Reduction JSBuiltinReducer::ReduceStringCharAt(Node* node) { } // Determine the {receiver} length. - Node* receiver_length = effect = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForStringLength()), receiver, - effect, control); + Node* receiver_length = + graph()->NewNode(simplified()->StringLength(), receiver); // Check if {index} is less than {receiver} length. Node* check = graph()->NewNode(simplified()->NumberLessThan(), index, @@ -2445,9 +2444,8 @@ Reduction JSBuiltinReducer::ReduceStringCharCodeAt(Node* node) { } // Determine the {receiver} length. - Node* receiver_length = effect = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForStringLength()), receiver, - effect, control); + Node* receiver_length = + graph()->NewNode(simplified()->StringLength(), receiver); // Check if {index} is less than {receiver} length. Node* check = graph()->NewNode(simplified()->NumberLessThan(), index, @@ -2577,9 +2575,7 @@ Reduction JSBuiltinReducer::ReduceStringIteratorNext(Node* node) { Node* index = effect = graph()->NewNode( simplified()->LoadField(AccessBuilder::ForJSStringIteratorIndex()), receiver, effect, control); - Node* length = effect = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForStringLength()), string, - effect, control); + Node* length = graph()->NewNode(simplified()->StringLength(), string); // branch0: if (index < length) Node* check0 = @@ -2670,9 +2666,8 @@ Reduction JSBuiltinReducer::ReduceStringIteratorNext(Node* node) { simplified()->StringFromCodePoint(UnicodeEncoding::UTF16), vtrue0); // Update iterator.[[NextIndex]] - Node* char_length = etrue0 = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForStringLength()), vtrue0, - etrue0, if_true0); + Node* char_length = + graph()->NewNode(simplified()->StringLength(), vtrue0); index = graph()->NewNode(simplified()->NumberAdd(), index, char_length); etrue0 = graph()->NewNode( simplified()->StoreField(AccessBuilder::ForJSStringIteratorIndex()), @@ -2721,9 +2716,8 @@ Reduction JSBuiltinReducer::ReduceStringSlice(Node* node) { if (start_type->Is(type_cache_.kSingletonMinusOne) && end_type->Is(Type::Undefined())) { - Node* receiver_length = effect = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForStringLength()), receiver, - effect, control); + Node* receiver_length = + graph()->NewNode(simplified()->StringLength(), receiver); Node* check = graph()->NewNode(simplified()->NumberEqual(), receiver_length, diff --git a/src/compiler/js-native-context-specialization.cc b/src/compiler/js-native-context-specialization.cc index 94b02d6f92..a359d580ab 100644 --- a/src/compiler/js-native-context-specialization.cc +++ b/src/compiler/js-native-context-specialization.cc @@ -1065,9 +1065,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess( effect, control); // Determine the {receiver} length. - Node* length = effect = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForStringLength()), receiver, - effect, control); + Node* length = graph()->NewNode(simplified()->StringLength(), receiver); // Load the single character string from {receiver} or yield undefined // if the {index} is out of bounds (depending on the {load_mode}). diff --git a/src/compiler/js-typed-lowering.cc b/src/compiler/js-typed-lowering.cc index 73180c644c..a4f9959d70 100644 --- a/src/compiler/js-typed-lowering.cc +++ b/src/compiler/js-typed-lowering.cc @@ -648,8 +648,8 @@ Reduction JSTypedLowering::ReduceCreateConsString(Node* node) { } // Determine the {first} length. - Node* first_length = BuildGetStringLength(first, &effect, control); - Node* second_length = BuildGetStringLength(second, &effect, control); + Node* first_length = BuildGetStringLength(first); + Node* second_length = BuildGetStringLength(second); // Compute the resulting length. Node* length = @@ -708,15 +708,14 @@ Reduction JSTypedLowering::ReduceCreateConsString(Node* node) { return Replace(value); } -Node* JSTypedLowering::BuildGetStringLength(Node* value, Node** effect, - Node* control) { +Node* JSTypedLowering::BuildGetStringLength(Node* value) { + // TODO(bmeurer): Get rid of this hack and instead have a way to + // express the string length in the types. HeapObjectMatcher m(value); Node* length = (m.HasValue() && m.Value()->IsString()) ? jsgraph()->Constant(Handle::cast(m.Value())->length()) - : (*effect) = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForStringLength()), - value, *effect, control); + : graph()->NewNode(simplified()->StringLength(), value); return length; } @@ -1118,16 +1117,12 @@ Reduction JSTypedLowering::ReduceJSLoadNamed(Node* node) { DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode()); Node* receiver = NodeProperties::GetValueInput(node, 0); Type* receiver_type = NodeProperties::GetType(receiver); - Node* effect = NodeProperties::GetEffectInput(node); - Node* control = NodeProperties::GetControlInput(node); Handle name = NamedAccessOf(node->op()).name(); // Optimize "length" property of strings. if (name.is_identical_to(factory()->length_string()) && receiver_type->Is(Type::String())) { - Node* value = effect = graph()->NewNode( - simplified()->LoadField(AccessBuilder::ForStringLength()), receiver, - effect, control); - ReplaceWithValue(node, value, effect); + Node* value = graph()->NewNode(simplified()->StringLength(), receiver); + ReplaceWithValue(node, value); return Replace(value); } return NoChange(); diff --git a/src/compiler/js-typed-lowering.h b/src/compiler/js-typed-lowering.h index 8b00c1d32c..0995e4ee44 100644 --- a/src/compiler/js-typed-lowering.h +++ b/src/compiler/js-typed-lowering.h @@ -85,8 +85,8 @@ class V8_EXPORT_PRIVATE JSTypedLowering final // Helper for ReduceJSLoadModule and ReduceJSStoreModule. Node* BuildGetModuleCell(Node* node); - // Helpers for ReduceJSCreateConsString and ReduceJSStringConcat. - Node* BuildGetStringLength(Node* value, Node** effect, Node* control); + // Helpers for ReduceJSCreateConsString. + Node* BuildGetStringLength(Node* value); Factory* factory() const; Graph* graph() const; diff --git a/src/compiler/opcodes.h b/src/compiler/opcodes.h index 77c05ad472..cc11044dae 100644 --- a/src/compiler/opcodes.h +++ b/src/compiler/opcodes.h @@ -335,6 +335,7 @@ V(StringFromCharCode) \ V(StringFromCodePoint) \ V(StringIndexOf) \ + V(StringLength) \ V(StringToLowerCaseIntl) \ V(StringToUpperCaseIntl) \ V(CheckBounds) \ diff --git a/src/compiler/simplified-lowering.cc b/src/compiler/simplified-lowering.cc index b3d08d485f..f3d4706de0 100644 --- a/src/compiler/simplified-lowering.cc +++ b/src/compiler/simplified-lowering.cc @@ -2415,6 +2415,14 @@ class RepresentationSelector { SetOutput(node, MachineRepresentation::kTaggedSigned); return; } + case IrOpcode::kStringLength: { + // TODO(bmeurer): The input representation should be TaggedPointer. + // Fix this once we have a dedicated StringConcat/JSStringAdd + // operator, which marks it's output as TaggedPointer properly. + VisitUnop(node, UseInfo::AnyTagged(), + MachineRepresentation::kTaggedSigned); + return; + } case IrOpcode::kStringToLowerCaseIntl: case IrOpcode::kStringToUpperCaseIntl: { VisitUnop(node, UseInfo::AnyTagged(), diff --git a/src/compiler/simplified-operator.cc b/src/compiler/simplified-operator.cc index 667323e867..0a32243187 100644 --- a/src/compiler/simplified-operator.cc +++ b/src/compiler/simplified-operator.cc @@ -590,6 +590,7 @@ DeoptimizeReason DeoptimizeReasonOf(const Operator* op) { V(SeqStringCharCodeAt, Operator::kNoProperties, 2, 1) \ V(StringFromCharCode, Operator::kNoProperties, 1, 0) \ V(StringIndexOf, Operator::kNoProperties, 3, 0) \ + V(StringLength, Operator::kNoProperties, 1, 0) \ V(StringToLowerCaseIntl, Operator::kNoProperties, 1, 0) \ V(StringToUpperCaseIntl, Operator::kNoProperties, 1, 0) \ V(TypeOf, Operator::kNoProperties, 1, 1) \ diff --git a/src/compiler/simplified-operator.h b/src/compiler/simplified-operator.h index 5af87a741f..03bd455267 100644 --- a/src/compiler/simplified-operator.h +++ b/src/compiler/simplified-operator.h @@ -405,6 +405,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final const Operator* StringFromCharCode(); const Operator* StringFromCodePoint(UnicodeEncoding encoding); const Operator* StringIndexOf(); + const Operator* StringLength(); const Operator* StringToLowerCaseIntl(); const Operator* StringToUpperCaseIntl(); diff --git a/src/compiler/typer.cc b/src/compiler/typer.cc index 2238992564..7ac4008227 100644 --- a/src/compiler/typer.cc +++ b/src/compiler/typer.cc @@ -1978,6 +1978,10 @@ Type* Typer::Visitor::TypeStringFromCodePoint(Node* node) { Type* Typer::Visitor::TypeStringIndexOf(Node* node) { UNREACHABLE(); } +Type* Typer::Visitor::TypeStringLength(Node* node) { + return typer_->cache_.kStringLengthType; +} + Type* Typer::Visitor::TypeCheckBounds(Node* node) { Type* index = Operand(node, 0); Type* length = Operand(node, 1); diff --git a/src/compiler/verifier.cc b/src/compiler/verifier.cc index 35f7b1493f..1d46d805c2 100644 --- a/src/compiler/verifier.cc +++ b/src/compiler/verifier.cc @@ -1058,6 +1058,10 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) { CheckValueInputIs(node, 2, Type::SignedSmall()); CheckTypeIs(node, Type::SignedSmall()); break; + case IrOpcode::kStringLength: + CheckValueInputIs(node, 0, Type::String()); + CheckTypeIs(node, TypeCache::Get().kStringLengthType); + break; case IrOpcode::kStringToLowerCaseIntl: case IrOpcode::kStringToUpperCaseIntl: CheckValueInputIs(node, 0, Type::String()); diff --git a/test/unittests/compiler/js-typed-lowering-unittest.cc b/test/unittests/compiler/js-typed-lowering-unittest.cc index 485efd6288..b07dbfd0dc 100644 --- a/test/unittests/compiler/js-typed-lowering-unittest.cc +++ b/test/unittests/compiler/js-typed-lowering-unittest.cc @@ -382,8 +382,7 @@ TEST_F(JSTypedLoweringTest, JSLoadNamedStringLength) { Reduce(graph()->NewNode(javascript()->LoadNamed(name, feedback), receiver, context, EmptyFrameState(), effect, control)); ASSERT_TRUE(r.Changed()); - EXPECT_THAT(r.replacement(), IsLoadField(AccessBuilder::ForStringLength(), - receiver, effect, control)); + EXPECT_THAT(r.replacement(), IsStringLength(receiver)); } diff --git a/test/unittests/compiler/node-test-utils.cc b/test/unittests/compiler/node-test-utils.cc index 52fd02b0a6..8e7084d1b1 100644 --- a/test/unittests/compiler/node-test-utils.cc +++ b/test/unittests/compiler/node-test-utils.cc @@ -2182,6 +2182,7 @@ IS_UNOP_MATCHER(ObjectIsReceiver) IS_UNOP_MATCHER(ObjectIsSmi) IS_UNOP_MATCHER(ObjectIsUndetectable) IS_UNOP_MATCHER(StringFromCharCode) +IS_UNOP_MATCHER(StringLength) IS_UNOP_MATCHER(Word32Clz) IS_UNOP_MATCHER(Word32Ctz) IS_UNOP_MATCHER(Word32Popcnt) diff --git a/test/unittests/compiler/node-test-utils.h b/test/unittests/compiler/node-test-utils.h index 81e471f30f..3ce6aba0f3 100644 --- a/test/unittests/compiler/node-test-utils.h +++ b/test/unittests/compiler/node-test-utils.h @@ -272,6 +272,7 @@ Matcher IsNumberTan(const Matcher& value_matcher); Matcher IsNumberTanh(const Matcher& value_matcher); Matcher IsNumberTrunc(const Matcher& value_matcher); Matcher IsStringFromCharCode(const Matcher& value_matcher); +Matcher IsStringLength(const Matcher& value_matcher); Matcher IsAllocate(const Matcher& size_matcher, const Matcher& effect_matcher, const Matcher& control_matcher);