[turbofan] Optimize access to the length property of functions
When compiling to JavaScript a language that supports curryfication, it is convenient to be able to efficiently get the arity of a function to check for partial application. Change-Id: I6611b523b2c3795f1f8fb123f63f5b6d604d793d Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4111447 Reviewed-by: Jakob Linke <jgruber@chromium.org> Commit-Queue: Jakob Linke <jgruber@chromium.org> Cr-Commit-Position: refs/heads/main@{#85409}
This commit is contained in:
parent
48e79783ee
commit
7eb8937bca
1
AUTHORS
1
AUTHORS
@ -147,6 +147,7 @@ Jan de Mooij <jandemooij@gmail.com>
|
|||||||
Janusz Majnert <jmajnert@gmail.com>
|
Janusz Majnert <jmajnert@gmail.com>
|
||||||
Javad Amiri <javad.amiri@anu.edu.au>
|
Javad Amiri <javad.amiri@anu.edu.au>
|
||||||
Jay Freeman <saurik@saurik.com>
|
Jay Freeman <saurik@saurik.com>
|
||||||
|
Jérôme Vouillon <jerome.vouillon@gmail.com>
|
||||||
Jesper van den Ende <jespertheend@gmail.com>
|
Jesper van den Ende <jespertheend@gmail.com>
|
||||||
Ji Qiu <qiuji@iscas.ac.cn>
|
Ji Qiu <qiuji@iscas.ac.cn>
|
||||||
Jiawen Geng <technicalcute@gmail.com>
|
Jiawen Geng <technicalcute@gmail.com>
|
||||||
|
@ -213,6 +213,19 @@ FieldAccess AccessBuilder::ForJSFunctionSharedFunctionInfo() {
|
|||||||
return access;
|
return access;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
FieldAccess AccessBuilder::ForSharedFunctionInfoLength() {
|
||||||
|
FieldAccess access = {kTaggedBase,
|
||||||
|
SharedFunctionInfo::kLengthOffset,
|
||||||
|
Handle<Name>(),
|
||||||
|
MaybeHandle<Map>(),
|
||||||
|
TypeCache::Get()->kArgumentsLengthType,
|
||||||
|
MachineType::Uint16(),
|
||||||
|
kNoWriteBarrier,
|
||||||
|
"SharedFunctionInfoLength"};
|
||||||
|
return access;
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
FieldAccess AccessBuilder::ForJSFunctionFeedbackCell() {
|
FieldAccess AccessBuilder::ForJSFunctionFeedbackCell() {
|
||||||
FieldAccess access = {kTaggedBase, JSFunction::kFeedbackCellOffset,
|
FieldAccess access = {kTaggedBase, JSFunction::kFeedbackCellOffset,
|
||||||
|
@ -92,6 +92,9 @@ class V8_EXPORT_PRIVATE AccessBuilder final
|
|||||||
// Provides access to JSFunction::shared() field.
|
// Provides access to JSFunction::shared() field.
|
||||||
static FieldAccess ForJSFunctionSharedFunctionInfo();
|
static FieldAccess ForJSFunctionSharedFunctionInfo();
|
||||||
|
|
||||||
|
// Provides access to SharedFunctionInfo::length() field.
|
||||||
|
static FieldAccess ForSharedFunctionInfoLength();
|
||||||
|
|
||||||
// Provides access to JSFunction::feedback_cell() field.
|
// Provides access to JSFunction::feedback_cell() field.
|
||||||
static FieldAccess ForJSFunctionFeedbackCell();
|
static FieldAccess ForJSFunctionFeedbackCell();
|
||||||
|
|
||||||
|
@ -161,6 +161,12 @@ PropertyAccessInfo PropertyAccessInfo::StringLength(Zone* zone,
|
|||||||
return PropertyAccessInfo(zone, kStringLength, {}, {{receiver_map}, zone});
|
return PropertyAccessInfo(zone, kStringLength, {}, {{receiver_map}, zone});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
PropertyAccessInfo PropertyAccessInfo::FunctionLength(Zone* zone,
|
||||||
|
MapRef receiver_map) {
|
||||||
|
return PropertyAccessInfo(zone, kFunctionLength, {}, {{receiver_map}, zone});
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
PropertyAccessInfo PropertyAccessInfo::DictionaryProtoDataConstant(
|
PropertyAccessInfo PropertyAccessInfo::DictionaryProtoDataConstant(
|
||||||
Zone* zone, MapRef receiver_map, JSObjectRef holder,
|
Zone* zone, MapRef receiver_map, JSObjectRef holder,
|
||||||
@ -343,7 +349,8 @@ bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that,
|
|||||||
}
|
}
|
||||||
|
|
||||||
case kNotFound:
|
case kNotFound:
|
||||||
case kStringLength: {
|
case kStringLength:
|
||||||
|
case kFunctionLength: {
|
||||||
DCHECK(unrecorded_dependencies_.empty());
|
DCHECK(unrecorded_dependencies_.empty());
|
||||||
DCHECK(that->unrecorded_dependencies_.empty());
|
DCHECK(that->unrecorded_dependencies_.empty());
|
||||||
AppendVector(&lookup_start_object_maps_, that->lookup_start_object_maps_);
|
AppendVector(&lookup_start_object_maps_, that->lookup_start_object_maps_);
|
||||||
@ -1042,6 +1049,14 @@ PropertyAccessInfo AccessInfoFactory::LookupSpecialFieldAccessor(
|
|||||||
}
|
}
|
||||||
return Invalid();
|
return Invalid();
|
||||||
}
|
}
|
||||||
|
// Check for JSFunction::length field accessor.
|
||||||
|
if (map.object()->IsJSFunctionMap()) {
|
||||||
|
if (Name::Equals(isolate(), name.object(),
|
||||||
|
isolate()->factory()->length_string())) {
|
||||||
|
return PropertyAccessInfo::FunctionLength(zone(), map);
|
||||||
|
}
|
||||||
|
return Invalid();
|
||||||
|
}
|
||||||
// Check for special JSObject field accessors.
|
// Check for special JSObject field accessors.
|
||||||
FieldIndex field_index;
|
FieldIndex field_index;
|
||||||
if (Accessors::IsJSObjectFieldAccessor(isolate(), map.object(), name.object(),
|
if (Accessors::IsJSObjectFieldAccessor(isolate(), map.object(), name.object(),
|
||||||
|
@ -65,7 +65,8 @@ class PropertyAccessInfo final {
|
|||||||
kFastAccessorConstant,
|
kFastAccessorConstant,
|
||||||
kDictionaryProtoAccessorConstant,
|
kDictionaryProtoAccessorConstant,
|
||||||
kModuleExport,
|
kModuleExport,
|
||||||
kStringLength
|
kStringLength,
|
||||||
|
kFunctionLength
|
||||||
};
|
};
|
||||||
|
|
||||||
static PropertyAccessInfo NotFound(Zone* zone, MapRef receiver_map,
|
static PropertyAccessInfo NotFound(Zone* zone, MapRef receiver_map,
|
||||||
@ -91,6 +92,7 @@ class PropertyAccessInfo final {
|
|||||||
static PropertyAccessInfo ModuleExport(Zone* zone, MapRef receiver_map,
|
static PropertyAccessInfo ModuleExport(Zone* zone, MapRef receiver_map,
|
||||||
CellRef cell);
|
CellRef cell);
|
||||||
static PropertyAccessInfo StringLength(Zone* zone, MapRef receiver_map);
|
static PropertyAccessInfo StringLength(Zone* zone, MapRef receiver_map);
|
||||||
|
static PropertyAccessInfo FunctionLength(Zone* zone, MapRef receiver_map);
|
||||||
static PropertyAccessInfo Invalid(Zone* zone);
|
static PropertyAccessInfo Invalid(Zone* zone);
|
||||||
static PropertyAccessInfo DictionaryProtoDataConstant(
|
static PropertyAccessInfo DictionaryProtoDataConstant(
|
||||||
Zone* zone, MapRef receiver_map, JSObjectRef holder,
|
Zone* zone, MapRef receiver_map, JSObjectRef holder,
|
||||||
@ -113,6 +115,7 @@ class PropertyAccessInfo final {
|
|||||||
}
|
}
|
||||||
bool IsModuleExport() const { return kind() == kModuleExport; }
|
bool IsModuleExport() const { return kind() == kModuleExport; }
|
||||||
bool IsStringLength() const { return kind() == kStringLength; }
|
bool IsStringLength() const { return kind() == kStringLength; }
|
||||||
|
bool IsFunctionLength() const { return kind() == kFunctionLength; }
|
||||||
bool IsDictionaryProtoDataConstant() const {
|
bool IsDictionaryProtoDataConstant() const {
|
||||||
return kind() == kDictionaryProtoDataConstant;
|
return kind() == kDictionaryProtoDataConstant;
|
||||||
}
|
}
|
||||||
|
@ -180,6 +180,7 @@ class EffectControlLinearizer {
|
|||||||
Node* LowerStringEqual(Node* node);
|
Node* LowerStringEqual(Node* node);
|
||||||
Node* LowerStringLessThan(Node* node);
|
Node* LowerStringLessThan(Node* node);
|
||||||
Node* LowerStringLessThanOrEqual(Node* node);
|
Node* LowerStringLessThanOrEqual(Node* node);
|
||||||
|
Node* LowerFunctionLength(Node* node);
|
||||||
Node* LowerBigIntAdd(Node* node, Node* frame_state);
|
Node* LowerBigIntAdd(Node* node, Node* frame_state);
|
||||||
Node* LowerBigIntSubtract(Node* node, Node* frame_state);
|
Node* LowerBigIntSubtract(Node* node, Node* frame_state);
|
||||||
Node* LowerBigIntMultiply(Node* node, Node* frame_state);
|
Node* LowerBigIntMultiply(Node* node, Node* frame_state);
|
||||||
@ -1275,6 +1276,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
|
|||||||
case IrOpcode::kStringLessThanOrEqual:
|
case IrOpcode::kStringLessThanOrEqual:
|
||||||
result = LowerStringLessThanOrEqual(node);
|
result = LowerStringLessThanOrEqual(node);
|
||||||
break;
|
break;
|
||||||
|
case IrOpcode::kFunctionLength:
|
||||||
|
result = LowerFunctionLength(node);
|
||||||
|
break;
|
||||||
case IrOpcode::kBigIntAdd:
|
case IrOpcode::kBigIntAdd:
|
||||||
result = LowerBigIntAdd(node, frame_state);
|
result = LowerBigIntAdd(node, frame_state);
|
||||||
break;
|
break;
|
||||||
@ -4577,6 +4581,14 @@ Node* EffectControlLinearizer::LowerStringLessThanOrEqual(Node* node) {
|
|||||||
Builtins::CallableFor(isolate(), Builtin::kStringLessThanOrEqual), node);
|
Builtins::CallableFor(isolate(), Builtin::kStringLessThanOrEqual), node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Node* EffectControlLinearizer::LowerFunctionLength(Node* node) {
|
||||||
|
Node* subject = node->InputAt(0);
|
||||||
|
|
||||||
|
auto shared =
|
||||||
|
__ LoadField(AccessBuilder::ForJSFunctionSharedFunctionInfo(), subject);
|
||||||
|
return __ LoadField(AccessBuilder::ForSharedFunctionInfoLength(), shared);
|
||||||
|
}
|
||||||
|
|
||||||
Node* EffectControlLinearizer::LowerBigIntAdd(Node* node, Node* frame_state) {
|
Node* EffectControlLinearizer::LowerBigIntAdd(Node* node, Node* frame_state) {
|
||||||
Node* lhs = node->InputAt(0);
|
Node* lhs = node->InputAt(0);
|
||||||
Node* rhs = node->InputAt(1);
|
Node* rhs = node->InputAt(1);
|
||||||
|
@ -2798,6 +2798,9 @@ JSNativeContextSpecialization::BuildPropertyLoad(
|
|||||||
} else if (access_info.IsStringLength()) {
|
} else if (access_info.IsStringLength()) {
|
||||||
DCHECK_EQ(receiver, lookup_start_object);
|
DCHECK_EQ(receiver, lookup_start_object);
|
||||||
value = graph()->NewNode(simplified()->StringLength(), receiver);
|
value = graph()->NewNode(simplified()->StringLength(), receiver);
|
||||||
|
} else if (access_info.IsFunctionLength()) {
|
||||||
|
DCHECK_EQ(receiver, lookup_start_object);
|
||||||
|
value = graph()->NewNode(simplified()->FunctionLength(), receiver);
|
||||||
} else {
|
} else {
|
||||||
DCHECK(access_info.IsDataField() || access_info.IsFastDataConstant() ||
|
DCHECK(access_info.IsDataField() || access_info.IsFastDataConstant() ||
|
||||||
access_info.IsDictionaryProtoDataConstant());
|
access_info.IsDictionaryProtoDataConstant());
|
||||||
|
@ -523,6 +523,7 @@
|
|||||||
V(StringToLowerCaseIntl) \
|
V(StringToLowerCaseIntl) \
|
||||||
V(StringToNumber) \
|
V(StringToNumber) \
|
||||||
V(StringToUpperCaseIntl) \
|
V(StringToUpperCaseIntl) \
|
||||||
|
V(FunctionLength) \
|
||||||
V(ToBoolean) \
|
V(ToBoolean) \
|
||||||
V(TransitionAndStoreElement) \
|
V(TransitionAndStoreElement) \
|
||||||
V(TransitionAndStoreNonNumberElement) \
|
V(TransitionAndStoreNonNumberElement) \
|
||||||
|
@ -3595,6 +3595,11 @@ class RepresentationSelector {
|
|||||||
MachineRepresentation::kTaggedPointer);
|
MachineRepresentation::kTaggedPointer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
case IrOpcode::kFunctionLength: {
|
||||||
|
VisitUnop<T>(node, UseInfo::AnyTagged(),
|
||||||
|
MachineRepresentation::kWord32);
|
||||||
|
return;
|
||||||
|
}
|
||||||
case IrOpcode::kCheckBounds:
|
case IrOpcode::kCheckBounds:
|
||||||
return VisitCheckBounds<T>(node, lowering);
|
return VisitCheckBounds<T>(node, lowering);
|
||||||
case IrOpcode::kCheckHeapObject: {
|
case IrOpcode::kCheckHeapObject: {
|
||||||
|
@ -808,6 +808,7 @@ bool operator==(CheckMinusZeroParameters const& lhs,
|
|||||||
V(StringLength, Operator::kNoProperties, 1, 0) \
|
V(StringLength, Operator::kNoProperties, 1, 0) \
|
||||||
V(StringToLowerCaseIntl, Operator::kNoProperties, 1, 0) \
|
V(StringToLowerCaseIntl, Operator::kNoProperties, 1, 0) \
|
||||||
V(StringToUpperCaseIntl, Operator::kNoProperties, 1, 0) \
|
V(StringToUpperCaseIntl, Operator::kNoProperties, 1, 0) \
|
||||||
|
V(FunctionLength, Operator::kNoProperties, 1, 0) \
|
||||||
V(TypeOf, Operator::kNoProperties, 1, 1) \
|
V(TypeOf, Operator::kNoProperties, 1, 1) \
|
||||||
V(PlainPrimitiveToNumber, Operator::kNoProperties, 1, 0) \
|
V(PlainPrimitiveToNumber, Operator::kNoProperties, 1, 0) \
|
||||||
V(PlainPrimitiveToWord32, Operator::kNoProperties, 1, 0) \
|
V(PlainPrimitiveToWord32, Operator::kNoProperties, 1, 0) \
|
||||||
|
@ -911,6 +911,8 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
|
|||||||
const Operator* StringToUpperCaseIntl();
|
const Operator* StringToUpperCaseIntl();
|
||||||
const Operator* StringSubstring();
|
const Operator* StringSubstring();
|
||||||
|
|
||||||
|
const Operator* FunctionLength();
|
||||||
|
|
||||||
const Operator* FindOrderedHashMapEntryForInt32Key();
|
const Operator* FindOrderedHashMapEntryForInt32Key();
|
||||||
const Operator* FindOrderedCollectionEntry(CollectionKind collection_kind);
|
const Operator* FindOrderedCollectionEntry(CollectionKind collection_kind);
|
||||||
|
|
||||||
|
@ -2287,6 +2287,10 @@ Type Typer::Visitor::TypeStringLength(Node* node) {
|
|||||||
|
|
||||||
Type Typer::Visitor::TypeStringSubstring(Node* node) { return Type::String(); }
|
Type Typer::Visitor::TypeStringSubstring(Node* node) { return Type::String(); }
|
||||||
|
|
||||||
|
Type Typer::Visitor::TypeFunctionLength(Node* node) {
|
||||||
|
return Type::Range(0, InstructionStream::kMaxArguments, zone());
|
||||||
|
}
|
||||||
|
|
||||||
Type Typer::Visitor::TypeCheckBounds(Node* node) {
|
Type Typer::Visitor::TypeCheckBounds(Node* node) {
|
||||||
return typer_->operation_typer_.CheckBounds(Operand(node, 0),
|
return typer_->operation_typer_.CheckBounds(Operand(node, 0),
|
||||||
Operand(node, 1));
|
Operand(node, 1));
|
||||||
|
@ -1227,6 +1227,10 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
|
|||||||
CheckValueInputIs(node, 2, Type::SignedSmall());
|
CheckValueInputIs(node, 2, Type::SignedSmall());
|
||||||
CheckTypeIs(node, Type::String());
|
CheckTypeIs(node, Type::String());
|
||||||
break;
|
break;
|
||||||
|
case IrOpcode::kFunctionLength:
|
||||||
|
CheckValueInputIs(node, 0, Type::Any());
|
||||||
|
CheckTypeIs(node, TypeCache::Get()->kArgumentsLengthType);
|
||||||
|
break;
|
||||||
case IrOpcode::kReferenceEqual:
|
case IrOpcode::kReferenceEqual:
|
||||||
CheckTypeIs(node, Type::Boolean());
|
CheckTypeIs(node, Type::Boolean());
|
||||||
break;
|
break;
|
||||||
|
@ -2453,6 +2453,22 @@ void StringLength::GenerateCode(MaglevAssembler* masm,
|
|||||||
FieldMemOperand(object, String::kLengthOffset));
|
FieldMemOperand(object, String::kLengthOffset));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FunctionLength::SetValueLocationConstraints() {
|
||||||
|
UseRegister(object_input());
|
||||||
|
DefineAsRegister(this);
|
||||||
|
}
|
||||||
|
void FunctionLength::GenerateCode(MaglevAssembler* masm,
|
||||||
|
const ProcessingState& state) {
|
||||||
|
Register object = ToRegister(object_input());
|
||||||
|
__ AssertFunction(object);
|
||||||
|
UseScratchRegisterScope temps(masm);
|
||||||
|
Register shared = temps.AcquireX();
|
||||||
|
__ LoadTaggedPointerField(
|
||||||
|
shared, FieldMemOperand(object, JSFunction::kSharedFunctionInfoOffset));
|
||||||
|
__ Ldr(ToRegister(result()).W(),
|
||||||
|
FieldMemOperand(shared, SharedFunctionInfo::kLengthOffset));
|
||||||
|
}
|
||||||
|
|
||||||
void TestUndetectable::SetValueLocationConstraints() {
|
void TestUndetectable::SetValueLocationConstraints() {
|
||||||
UseRegister(value());
|
UseRegister(value());
|
||||||
DefineAsRegister(this);
|
DefineAsRegister(this);
|
||||||
|
@ -1960,6 +1960,13 @@ bool MaglevGraphBuilder::TryBuildPropertyLoad(
|
|||||||
current_interpreter_frame_.accumulator(),
|
current_interpreter_frame_.accumulator(),
|
||||||
access_info);
|
access_info);
|
||||||
return true;
|
return true;
|
||||||
|
case compiler::PropertyAccessInfo::kFunctionLength:
|
||||||
|
DCHECK_EQ(receiver, lookup_start_object);
|
||||||
|
SetAccumulator(AddNewNode<FunctionLength>({receiver}));
|
||||||
|
RecordKnownProperty(lookup_start_object, name,
|
||||||
|
current_interpreter_frame_.accumulator(),
|
||||||
|
access_info);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,6 +210,7 @@ class CompactInterpreterFrameState;
|
|||||||
V(SetPendingMessage) \
|
V(SetPendingMessage) \
|
||||||
V(StringAt) \
|
V(StringAt) \
|
||||||
V(StringLength) \
|
V(StringLength) \
|
||||||
|
V(FunctionLength) \
|
||||||
V(ToBoolean) \
|
V(ToBoolean) \
|
||||||
V(ToBooleanLogicalNot) \
|
V(ToBooleanLogicalNot) \
|
||||||
V(TaggedEqual) \
|
V(TaggedEqual) \
|
||||||
@ -4639,6 +4640,25 @@ class StringLength : public FixedInputValueNodeT<1, StringLength> {
|
|||||||
void PrintParams(std::ostream&, MaglevGraphLabeller*) const {}
|
void PrintParams(std::ostream&, MaglevGraphLabeller*) const {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FunctionLength : public FixedInputValueNodeT<1, FunctionLength> {
|
||||||
|
using Base = FixedInputValueNodeT<1, FunctionLength>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit FunctionLength(uint64_t bitfield) : Base(bitfield) {}
|
||||||
|
|
||||||
|
static constexpr OpProperties kProperties =
|
||||||
|
OpProperties::Reading() | OpProperties::Int32();
|
||||||
|
static constexpr
|
||||||
|
typename Base::InputTypes kInputTypes{ValueRepresentation::kTagged};
|
||||||
|
|
||||||
|
static constexpr int kObjectIndex = 0;
|
||||||
|
Input& object_input() { return input(kObjectIndex); }
|
||||||
|
|
||||||
|
void SetValueLocationConstraints();
|
||||||
|
void GenerateCode(MaglevAssembler*, const ProcessingState&);
|
||||||
|
void PrintParams(std::ostream&, MaglevGraphLabeller*) const {}
|
||||||
|
};
|
||||||
|
|
||||||
class DefineNamedOwnGeneric
|
class DefineNamedOwnGeneric
|
||||||
: public FixedInputValueNodeT<3, DefineNamedOwnGeneric> {
|
: public FixedInputValueNodeT<3, DefineNamedOwnGeneric> {
|
||||||
using Base = FixedInputValueNodeT<3, DefineNamedOwnGeneric>;
|
using Base = FixedInputValueNodeT<3, DefineNamedOwnGeneric>;
|
||||||
|
@ -1331,6 +1331,21 @@ void StringLength::GenerateCode(MaglevAssembler* masm,
|
|||||||
__ movl(ToRegister(result()), FieldOperand(object, String::kLengthOffset));
|
__ movl(ToRegister(result()), FieldOperand(object, String::kLengthOffset));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FunctionLength::SetValueLocationConstraints() {
|
||||||
|
UseRegister(object_input());
|
||||||
|
DefineAsRegister(this);
|
||||||
|
}
|
||||||
|
void FunctionLength::GenerateCode(MaglevAssembler* masm,
|
||||||
|
const ProcessingState& state) {
|
||||||
|
Register object = ToRegister(object_input());
|
||||||
|
__ AssertFunction(object);
|
||||||
|
__ LoadTaggedPointerField(
|
||||||
|
kScratchRegister,
|
||||||
|
FieldOperand(object, JSFunction::kSharedFunctionInfoOffset));
|
||||||
|
__ movl(ToRegister(result()),
|
||||||
|
FieldOperand(kScratchRegister, SharedFunctionInfo::kLengthOffset));
|
||||||
|
}
|
||||||
|
|
||||||
void Int32AddWithOverflow::SetValueLocationConstraints() {
|
void Int32AddWithOverflow::SetValueLocationConstraints() {
|
||||||
UseRegister(left_input());
|
UseRegister(left_input());
|
||||||
UseRegister(right_input());
|
UseRegister(right_input());
|
||||||
|
31
test/mjsunit/compiler/function-length.js
Normal file
31
test/mjsunit/compiler/function-length.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Copyright 2023 the V8 project authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
// Flags: --allow-natives-syntax --turbofan --no-always-turbofan
|
||||||
|
|
||||||
|
function f(g) {
|
||||||
|
return g.length;
|
||||||
|
}
|
||||||
|
function g(x, y) {}
|
||||||
|
function h(x, y, z) {}
|
||||||
|
function OptimizeAndTest(fn) {
|
||||||
|
%PrepareFunctionForOptimization(fn);
|
||||||
|
assertEquals(1, fn(f));
|
||||||
|
assertEquals(2, fn(g));
|
||||||
|
assertEquals(3, fn(h));
|
||||||
|
|
||||||
|
%OptimizeFunctionOnNextCall(fn);
|
||||||
|
fn(g);
|
||||||
|
assertOptimized(fn);
|
||||||
|
|
||||||
|
assertEquals(1, fn(f));
|
||||||
|
assertEquals(2, fn(g));
|
||||||
|
assertEquals(3, fn(h));
|
||||||
|
assertOptimized(fn);
|
||||||
|
|
||||||
|
assertEquals(3, fn('abc'));
|
||||||
|
assertUnoptimized(fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
OptimizeAndTest(f);
|
Loading…
Reference in New Issue
Block a user