From 38cb7ab0807f07200ba5b05dc7674161d184e464 Mon Sep 17 00:00:00 2001 From: Keeley Hammond Date: Tue, 18 Nov 2025 15:47:15 -0800 Subject: [PATCH] chore: cherry-pick 62af07e96173 from v8 (#49009) * chore: cherry-pick 62af07e96173 from v8 * chore: update patches * test: move to macos-14-large (needed for Intel) --- .github/workflows/build.yml | 2 +- patches/v8/.patches | 1 + patches/v8/cherry-pick-62af07e96173.patch | 405 ++++++++++++++++++++++ 3 files changed, 407 insertions(+), 1 deletion(-) create mode 100644 patches/v8/cherry-pick-62af07e96173.patch diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 28b71bd1d8..a2b39f6265 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -249,7 +249,7 @@ jobs: needs: checkout-macos with: build-runs-on: macos-14-xlarge - test-runs-on: macos-13 + test-runs-on: macos-14-large target-platform: macos target-arch: x64 is-release: false diff --git a/patches/v8/.patches b/patches/v8/.patches index 4bc1ea4511..e6c90d97b4 100644 --- a/patches/v8/.patches +++ b/patches/v8/.patches @@ -2,3 +2,4 @@ chore_allow_customizing_microtask_policy_per_context.patch turboshaft_avoid_introducing_too_many_variables.patch cherry-pick-4cf9311810b0.patch merged_maglev_fix_left_over_register_allocations_from_regalloc.patch +cherry-pick-62af07e96173.patch diff --git a/patches/v8/cherry-pick-62af07e96173.patch b/patches/v8/cherry-pick-62af07e96173.patch new file mode 100644 index 0000000000..9a8869b30d --- /dev/null +++ b/patches/v8/cherry-pick-62af07e96173.patch @@ -0,0 +1,405 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Igor Sheludko +Date: Mon, 13 Oct 2025 14:22:20 +0200 +Subject: [ic] Cleanup AccessorAssembler::CallGetterIfAccessor() + +This CL + - reorders parameters to make |expected_receiver_mode| a mandatory + one and properly computed, + - makes sure we don't pass PropertyCell as a holder when JSReceiver is + expected. + +Bug: 450328966 +Change-Id: I921dfbd99245d01143600b4f4713fe602c817657 +Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7036691 +Commit-Queue: Igor Sheludko +Reviewed-by: Leszek Swirski +Cr-Commit-Position: refs/heads/main@{#103085} + +diff --git a/src/codegen/code-stub-assembler.cc b/src/codegen/code-stub-assembler.cc +index f56eba583a0a2117fc895f12b7a0712f59e3a1b6..e27d74877d9ef9b9bfa958b9b49773fd7b3c3d08 100644 +--- a/src/codegen/code-stub-assembler.cc ++++ b/src/codegen/code-stub-assembler.cc +@@ -11549,7 +11549,8 @@ void CodeStubAssembler::ForEachEnumerableOwnProperty( + + var_value = CallGetterIfAccessor( + value_or_accessor, object, var_details.value(), context, +- object, next_key, &slow_load, kCallJSGetterUseCachedName); ++ object, kExpectingJSReceiver, next_key, &slow_load, ++ kCallJSGetterUseCachedName); + Goto(&value_ready); + + BIND(&slow_load); +@@ -12045,15 +12046,11 @@ template void CodeStubAssembler::LoadPropertyFromDictionary( + TNode dictionary, TNode name_index, + TVariable* var_details, TVariable* var_value); + +-// |value| is the property backing store's contents, which is either a value or +-// an accessor pair, as specified by |details|. |holder| is a JSReceiver or a +-// PropertyCell. Returns either the original value, or the result of the getter +-// call. + TNode CodeStubAssembler::CallGetterIfAccessor( +- TNode value, TNode> holder, ++ TNode value, std::optional> holder, + TNode details, TNode context, TNode receiver, +- TNode name, Label* if_bailout, GetOwnPropertyMode mode, +- ExpectedReceiverMode expected_receiver_mode) { ++ ExpectedReceiverMode expected_receiver_mode, TNode name, ++ Label* if_bailout, GetOwnPropertyMode mode) { + TVARIABLE(Object, var_value, value); + Label done(this), if_accessor_info(this, Label::kDeferred); + +@@ -12094,44 +12091,51 @@ TNode CodeStubAssembler::CallGetterIfAccessor( + + BIND(&if_function_template_info); + { +- Label use_cached_property(this); +- TNode cached_property_name = LoadObjectField( +- getter, FunctionTemplateInfo::kCachedPropertyNameOffset); +- +- Label* has_cached_property = mode == kCallJSGetterUseCachedName +- ? &use_cached_property +- : if_bailout; +- GotoIfNot(IsTheHole(cached_property_name), has_cached_property); +- +- TNode js_receiver; +- switch (expected_receiver_mode) { +- case kExpectingJSReceiver: +- js_receiver = CAST(receiver); +- break; +- case kExpectingAnyReceiver: +- // TODO(ishell): in case the function template info has a signature +- // and receiver is not a JSReceiver the signature check in +- // CallFunctionTemplate builtin will fail anyway, so we can short +- // cut it here and throw kIllegalInvocation immediately. +- js_receiver = ToObject_Inline(context, receiver); +- break; +- } +- TNode holder_receiver = CAST(holder); +- TNode creation_context = +- GetCreationContext(holder_receiver, if_bailout); +- TNode caller_context = context; +- var_value = CallBuiltin( +- Builtin::kCallFunctionTemplate_Generic, creation_context, getter, +- Int32Constant(i::JSParameterCount(0)), caller_context, js_receiver); +- Goto(&done); ++ if (holder.has_value()) { ++ Label use_cached_property(this); ++ TNode cached_property_name = LoadObjectField( ++ getter, FunctionTemplateInfo::kCachedPropertyNameOffset); ++ ++ Label* has_cached_property = mode == kCallJSGetterUseCachedName ++ ? &use_cached_property ++ : if_bailout; ++ GotoIfNot(IsTheHole(cached_property_name), has_cached_property); ++ ++ TNode js_receiver; ++ switch (expected_receiver_mode) { ++ case kExpectingJSReceiver: ++ js_receiver = CAST(receiver); ++ break; ++ case kExpectingAnyReceiver: ++ // TODO(ishell): in case the function template info has a ++ // signature and receiver is not a JSReceiver the signature check ++ // in CallFunctionTemplate builtin will fail anyway, so we can ++ // short cut it here and throw kIllegalInvocation immediately. ++ js_receiver = ToObject_Inline(context, receiver); ++ break; ++ } ++ TNode holder_receiver = *holder; ++ TNode creation_context = ++ GetCreationContext(holder_receiver, if_bailout); ++ TNode caller_context = context; ++ var_value = CallBuiltin(Builtin::kCallFunctionTemplate_Generic, ++ creation_context, getter, ++ Int32Constant(i::JSParameterCount(0)), ++ caller_context, js_receiver); ++ Goto(&done); + +- if (mode == kCallJSGetterUseCachedName) { +- Bind(&use_cached_property); ++ if (mode == kCallJSGetterUseCachedName) { ++ Bind(&use_cached_property); + +- var_value = +- GetProperty(context, holder_receiver, cached_property_name); ++ var_value = ++ GetProperty(context, holder_receiver, cached_property_name); + +- Goto(&done); ++ Goto(&done); ++ } ++ } else { ++ // |holder| must be available in order to handle lazy AccessorPair ++ // case (we need it for computing the function's context). ++ Unreachable(); + } + } + } else { +@@ -12143,56 +12147,61 @@ TNode CodeStubAssembler::CallGetterIfAccessor( + // AccessorInfo case. + BIND(&if_accessor_info); + { +- TNode accessor_info = CAST(value); +- Label if_array(this), if_function(this), if_wrapper(this); +- +- // Dispatch based on {holder} instance type. +- TNode holder_map = LoadMap(holder); +- TNode holder_instance_type = LoadMapInstanceType(holder_map); +- GotoIf(IsJSArrayInstanceType(holder_instance_type), &if_array); +- GotoIf(IsJSFunctionInstanceType(holder_instance_type), &if_function); +- Branch(IsJSPrimitiveWrapperInstanceType(holder_instance_type), &if_wrapper, +- if_bailout); +- +- // JSArray AccessorInfo case. +- BIND(&if_array); +- { +- // We only deal with the "length" accessor on JSArray. +- GotoIfNot(IsLengthString( +- LoadObjectField(accessor_info, AccessorInfo::kNameOffset)), +- if_bailout); +- TNode array = CAST(holder); +- var_value = LoadJSArrayLength(array); +- Goto(&done); +- } +- +- // JSFunction AccessorInfo case. +- BIND(&if_function); +- { +- // We only deal with the "prototype" accessor on JSFunction here. +- GotoIfNot(IsPrototypeString( +- LoadObjectField(accessor_info, AccessorInfo::kNameOffset)), +- if_bailout); ++ if (holder.has_value()) { ++ TNode accessor_info = CAST(value); ++ Label if_array(this), if_function(this), if_wrapper(this); ++ // Dispatch based on {holder} instance type. ++ TNode holder_map = LoadMap(*holder); ++ TNode holder_instance_type = LoadMapInstanceType(holder_map); ++ GotoIf(IsJSArrayInstanceType(holder_instance_type), &if_array); ++ GotoIf(IsJSFunctionInstanceType(holder_instance_type), &if_function); ++ Branch(IsJSPrimitiveWrapperInstanceType(holder_instance_type), ++ &if_wrapper, if_bailout); ++ ++ // JSArray AccessorInfo case. ++ BIND(&if_array); ++ { ++ // We only deal with the "length" accessor on JSArray. ++ GotoIfNot(IsLengthString(LoadObjectField(accessor_info, ++ AccessorInfo::kNameOffset)), ++ if_bailout); ++ TNode array = CAST(*holder); ++ var_value = LoadJSArrayLength(array); ++ Goto(&done); ++ } + +- TNode function = CAST(holder); +- GotoIfPrototypeRequiresRuntimeLookup(function, holder_map, if_bailout); +- var_value = LoadJSFunctionPrototype(function, if_bailout); +- Goto(&done); +- } ++ // JSFunction AccessorInfo case. ++ BIND(&if_function); ++ { ++ // We only deal with the "prototype" accessor on JSFunction here. ++ GotoIfNot(IsPrototypeString(LoadObjectField(accessor_info, ++ AccessorInfo::kNameOffset)), ++ if_bailout); ++ ++ TNode function = CAST(*holder); ++ GotoIfPrototypeRequiresRuntimeLookup(function, holder_map, if_bailout); ++ var_value = LoadJSFunctionPrototype(function, if_bailout); ++ Goto(&done); ++ } + +- // JSPrimitiveWrapper AccessorInfo case. +- BIND(&if_wrapper); +- { +- // We only deal with the "length" accessor on JSPrimitiveWrapper string +- // wrappers. +- GotoIfNot(IsLengthString( +- LoadObjectField(accessor_info, AccessorInfo::kNameOffset)), +- if_bailout); +- TNode holder_value = LoadJSPrimitiveWrapperValue(CAST(holder)); +- GotoIfNot(TaggedIsNotSmi(holder_value), if_bailout); +- GotoIfNot(IsString(CAST(holder_value)), if_bailout); +- var_value = LoadStringLengthAsSmi(CAST(holder_value)); +- Goto(&done); ++ // JSPrimitiveWrapper AccessorInfo case. ++ BIND(&if_wrapper); ++ { ++ // We only deal with the "length" accessor on JSPrimitiveWrapper string ++ // wrappers. ++ GotoIfNot(IsLengthString(LoadObjectField(accessor_info, ++ AccessorInfo::kNameOffset)), ++ if_bailout); ++ TNode holder_value = LoadJSPrimitiveWrapperValue(CAST(*holder)); ++ GotoIfNot(TaggedIsNotSmi(holder_value), if_bailout); ++ GotoIfNot(IsString(CAST(holder_value)), if_bailout); ++ var_value = LoadStringLengthAsSmi(CAST(holder_value)); ++ Goto(&done); ++ } ++ } else { ++ // |holder| must be available in order to handle AccessorInfo case (we ++ // need to pass it to the callback). ++ Unreachable(); + } + } + +@@ -12277,7 +12286,7 @@ void CodeStubAssembler::TryGetOwnProperty( + } + TNode value = CallGetterIfAccessor( + var_value->value(), object, var_details->value(), context, receiver, +- unique_name, if_bailout, mode, expected_receiver_mode); ++ expected_receiver_mode, unique_name, if_bailout, mode); + *var_value = value; + Goto(if_found_value); + } +diff --git a/src/codegen/code-stub-assembler.h b/src/codegen/code-stub-assembler.h +index 506442eddf629a893a6661ff87bdf85c21d93445..f143592226c4009167e38e1b883e3448eb588776 100644 +--- a/src/codegen/code-stub-assembler.h ++++ b/src/codegen/code-stub-assembler.h +@@ -4582,12 +4582,16 @@ class V8_EXPORT_PRIVATE CodeStubAssembler + const ForEachKeyValueFunction& body, + Label* bailout); + ++ // |value| is the property backing store's contents, which is either a value ++ // or an accessor pair, as specified by |details|. |holder| is a JSReceiver ++ // or empty std::nullopt if holder is not available. ++ // Returns either the original value, or the result of the getter call. + TNode CallGetterIfAccessor( +- TNode value, TNode> holder, ++ TNode value, std::optional> holder, + TNode details, TNode context, TNode receiver, +- TNode name, Label* if_bailout, +- GetOwnPropertyMode mode = kCallJSGetterDontUseCachedName, +- ExpectedReceiverMode expected_receiver_mode = kExpectingJSReceiver); ++ ExpectedReceiverMode expected_receiver_mode, TNode name, ++ Label* if_bailout, ++ GetOwnPropertyMode mode = kCallJSGetterDontUseCachedName); + + TNode TryToIntptr(TNode key, Label* if_not_intptr, + TVariable* var_instance_type = nullptr); +diff --git a/src/ic/accessor-assembler.cc b/src/ic/accessor-assembler.cc +index c1cd3790f12418ecf7d3f027475abde3b8e2dedd..457fbc25d91e740190662a564ba315e390830b7f 100644 +--- a/src/ic/accessor-assembler.cc ++++ b/src/ic/accessor-assembler.cc +@@ -845,9 +845,13 @@ void AccessorAssembler::HandleLoadICSmiHandlerLoadNamedCase( + TVARIABLE(Object, var_value); + LoadPropertyFromDictionary( + properties, var_name_index.value(), &var_details, &var_value); ++ ++ ExpectedReceiverMode expected_receiver_mode = ++ p->IsLoadSuperIC() ? kExpectingAnyReceiver : kExpectingJSReceiver; ++ + TNode value = CallGetterIfAccessor( + var_value.value(), CAST(holder), var_details.value(), p->context(), +- p->receiver(), p->name(), miss); ++ p->receiver(), expected_receiver_mode, p->name(), miss); + exit_point->Return(value); + } + } +@@ -925,17 +929,18 @@ void AccessorAssembler::HandleLoadICSmiHandlerLoadNamedCase( + + BIND(&global); + { +- CSA_DCHECK(this, IsPropertyCell(CAST(holder))); + // Ensure the property cell doesn't contain the hole. +- TNode value = +- LoadObjectField(CAST(holder), PropertyCell::kValueOffset); ++ TNode value = LoadPropertyCellValue(CAST(holder)); ++ GotoIf(IsPropertyCellHole(value), miss); + TNode details = Unsigned(LoadAndUntagToWord32ObjectField( + CAST(holder), PropertyCell::kPropertyDetailsRawOffset)); +- GotoIf(IsPropertyCellHole(value), miss); + +- exit_point->Return(CallGetterIfAccessor(value, CAST(holder), details, +- p->context(), p->receiver(), +- p->name(), miss)); ++ ExpectedReceiverMode expected_receiver_mode = ++ p->IsLoadSuperIC() ? kExpectingAnyReceiver : kExpectingJSReceiver; ++ ++ exit_point->Return(CallGetterIfAccessor( ++ value, std::nullopt, details, p->context(), p->receiver(), ++ expected_receiver_mode, p->name(), miss)); + } + + BIND(&interceptor); +@@ -1221,9 +1226,14 @@ void AccessorAssembler::HandleLoadICProtoHandler( + TVARIABLE(Object, var_value); + LoadPropertyFromDictionary( + properties, name_index, &var_details, &var_value); ++ ++ ExpectedReceiverMode expected_receiver_mode = ++ p->IsLoadSuperIC() ? kExpectingAnyReceiver : kExpectingJSReceiver; ++ + TNode value = CallGetterIfAccessor( + var_value.value(), CAST(var_holder->value()), var_details.value(), +- p->context(), p->receiver(), p->name(), miss); ++ p->context(), p->receiver(), expected_receiver_mode, p->name(), ++ miss); + exit_point->Return(value); + } + }, +@@ -2960,9 +2970,12 @@ void AccessorAssembler::GenericPropertyLoad( + + BIND(&if_found_on_lookup_start_object); + { ++ ExpectedReceiverMode expected_receiver_mode = ++ p->IsLoadSuperIC() ? kExpectingAnyReceiver : kExpectingJSReceiver; ++ + TNode value = CallGetterIfAccessor( + var_value.value(), CAST(lookup_start_object), var_details.value(), +- p->context(), p->receiver(), p->name(), slow); ++ p->context(), p->receiver(), expected_receiver_mode, p->name(), slow); + Return(value); + } + +diff --git a/src/ic/accessor-assembler.h b/src/ic/accessor-assembler.h +index 29cbf283ff143d1c62ba13e93e3b4fbef0931226..30bb186feb0cec20228b27aadd1c26949188e991 100644 +--- a/src/ic/accessor-assembler.h ++++ b/src/ic/accessor-assembler.h +@@ -138,9 +138,7 @@ class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler { + TNode name() const { return name_; } + TNode slot() const { return slot_; } + TNode vector() const { return vector_; } +- TNode lookup_start_object() const { +- return lookup_start_object_.value(); +- } ++ TNode lookup_start_object() const { return lookup_start_object_; } + TNode enum_index() const { return *enum_index_; } + TNode cache_type() const { return *cache_type_; } + +@@ -152,6 +150,11 @@ class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler { + return receiver_; + } + ++ // This is useful for figuring out whether we know anything about receiver ++ // type. If |receiver| and |lookup_start_object| are different TNodes ++ // then this ICParameters object belongs to LoadSuperIC. ++ bool IsLoadSuperIC() const { return lookup_start_object_ != receiver_; } ++ + bool IsEnumeratedKeyedLoad() const { return enum_index_ != std::nullopt; } + + private: +@@ -160,7 +163,7 @@ class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler { + TNode name_; + TNode slot_; + TNode vector_; +- std::optional> lookup_start_object_; ++ TNode lookup_start_object_; + std::optional> enum_index_; + std::optional> cache_type_; + }; +@@ -202,6 +205,11 @@ class V8_EXPORT_PRIVATE AccessorAssembler : public CodeStubAssembler { + return receiver_; + } + ++ // This is useful for figuring out whether we know anything about receiver ++ // type. If |receiver| and |lookup_start_object| are different TNodes ++ // then this ICParameters object belongs to LoadSuperIC. ++ bool IsLoadSuperIC() const { return lookup_start_object_ != receiver_; } ++ + private: + LazyNode context_; + TNode receiver_;