Commit 7eeac39f authored by Georg Neis's avatar Georg Neis Committed by Commit Bot

[compiler] Check for stack overflow when unrolling JSBoundFunctions

Gracefully handle hugely nested JSBoundFunctions by checking against
the local isolate's stack limit in relevant recursive functions.

This is based on d734bb4c (which was
reverted).

In order to get access to the local isolate, the CL replaces the heap
broker's LocalHeap pointer with a LocalIsolate pointer.

Bug: chromium:1125145
Change-Id: I15d6265c7dfcd8a70af4ab4ce6f30149a886be00
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2480682
Commit-Queue: Georg Neis <neis@chromium.org>
Reviewed-by: 's avatarMaya Lekova <mslekova@chromium.org>
Reviewed-by: 's avatarDominik Inführ <dinfuehr@chromium.org>
Reviewed-by: 's avatarSantiago Aboy Solanes <solanes@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70654}
parent 7413658c
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "src/utils/boxed-float.h" #include "src/utils/boxed-float.h"
namespace v8 { namespace v8 {
class CFunctionInfo; class CFunctionInfo;
namespace internal { namespace internal {
...@@ -32,6 +33,7 @@ class NativeContext; ...@@ -32,6 +33,7 @@ class NativeContext;
class ScriptContextTable; class ScriptContextTable;
namespace compiler { namespace compiler {
// Whether we are loading a property or storing to a property. // Whether we are loading a property or storing to a property.
// For a store during literal creation, do not walk up the prototype chain. // For a store during literal creation, do not walk up the prototype chain.
enum class AccessMode { kLoad, kStore, kStoreInLiteral, kHas }; enum class AccessMode { kLoad, kStore, kStoreInLiteral, kHas };
...@@ -317,7 +319,7 @@ class JSBoundFunctionRef : public JSObjectRef { ...@@ -317,7 +319,7 @@ class JSBoundFunctionRef : public JSObjectRef {
Handle<JSBoundFunction> object() const; Handle<JSBoundFunction> object() const;
void Serialize(); bool Serialize();
bool serialized() const; bool serialized() const;
// The following are available only after calling Serialize(). // The following are available only after calling Serialize().
......
...@@ -3990,6 +3990,8 @@ bool JSCallReducer::IsBuiltinOrApiFunction(JSFunctionRef function) const { ...@@ -3990,6 +3990,8 @@ bool JSCallReducer::IsBuiltinOrApiFunction(JSFunctionRef function) const {
} }
Reduction JSCallReducer::ReduceJSCall(Node* node) { Reduction JSCallReducer::ReduceJSCall(Node* node) {
if (broker()->StackHasOverflowed()) return NoChange();
JSCallNode n(node); JSCallNode n(node);
CallParameters const& p = n.Parameters(); CallParameters const& p = n.Parameters();
Node* target = n.target(); Node* target = n.target();
......
...@@ -628,7 +628,7 @@ class JSBoundFunctionData : public JSObjectData { ...@@ -628,7 +628,7 @@ class JSBoundFunctionData : public JSObjectData {
JSBoundFunctionData(JSHeapBroker* broker, ObjectData** storage, JSBoundFunctionData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSBoundFunction> object); Handle<JSBoundFunction> object);
void Serialize(JSHeapBroker* broker); bool Serialize(JSHeapBroker* broker);
bool serialized() const { return serialized_; } bool serialized() const { return serialized_; }
ObjectData* bound_target_function() const { return bound_target_function_; } ObjectData* bound_target_function() const { return bound_target_function_; }
...@@ -1514,19 +1514,24 @@ JSBoundFunctionData::JSBoundFunctionData(JSHeapBroker* broker, ...@@ -1514,19 +1514,24 @@ JSBoundFunctionData::JSBoundFunctionData(JSHeapBroker* broker,
Handle<JSBoundFunction> object) Handle<JSBoundFunction> object)
: JSObjectData(broker, storage, object) {} : JSObjectData(broker, storage, object) {}
void JSBoundFunctionData::Serialize(JSHeapBroker* broker) { bool JSBoundFunctionData::Serialize(JSHeapBroker* broker) {
if (serialized_) return; if (serialized_) return true;
serialized_ = true; if (broker->StackHasOverflowed()) return false;
TraceScope tracer(broker, this, "JSBoundFunctionData::Serialize"); TraceScope tracer(broker, this, "JSBoundFunctionData::Serialize");
Handle<JSBoundFunction> function = Handle<JSBoundFunction>::cast(object()); Handle<JSBoundFunction> function = Handle<JSBoundFunction>::cast(object());
// We set {serialized_} at the end in order to correctly handle the case where
// a recursive call to this method reaches the stack limit.
bool serialized = true;
DCHECK_NULL(bound_target_function_); DCHECK_NULL(bound_target_function_);
bound_target_function_ = bound_target_function_ =
broker->GetOrCreateData(function->bound_target_function()); broker->GetOrCreateData(function->bound_target_function());
if (!bound_target_function_->should_access_heap()) { if (!bound_target_function_->should_access_heap()) {
if (bound_target_function_->IsJSBoundFunction()) { if (bound_target_function_->IsJSBoundFunction()) {
bound_target_function_->AsJSBoundFunction()->Serialize(broker); serialized =
bound_target_function_->AsJSBoundFunction()->Serialize(broker);
} else if (bound_target_function_->IsJSFunction()) { } else if (bound_target_function_->IsJSFunction()) {
bound_target_function_->AsJSFunction()->Serialize(broker); bound_target_function_->AsJSFunction()->Serialize(broker);
} }
...@@ -1540,6 +1545,9 @@ void JSBoundFunctionData::Serialize(JSHeapBroker* broker) { ...@@ -1540,6 +1545,9 @@ void JSBoundFunctionData::Serialize(JSHeapBroker* broker) {
DCHECK_NULL(bound_this_); DCHECK_NULL(bound_this_);
bound_this_ = broker->GetOrCreateData(function->bound_this()); bound_this_ = broker->GetOrCreateData(function->bound_this());
serialized_ = serialized;
return serialized;
} }
JSObjectData::JSObjectData(JSHeapBroker* broker, ObjectData** storage, JSObjectData::JSObjectData(JSHeapBroker* broker, ObjectData** storage,
...@@ -2441,7 +2449,7 @@ JSHeapBroker::JSHeapBroker(Isolate* isolate, Zone* broker_zone, ...@@ -2441,7 +2449,7 @@ JSHeapBroker::JSHeapBroker(Isolate* isolate, Zone* broker_zone,
TRACE(this, "Constructing heap broker"); TRACE(this, "Constructing heap broker");
} }
JSHeapBroker::~JSHeapBroker() { DCHECK(!local_heap_); } JSHeapBroker::~JSHeapBroker() { DCHECK_NULL(local_isolate_); }
void JSHeapBroker::SetPersistentAndCopyCanonicalHandlesForTesting( void JSHeapBroker::SetPersistentAndCopyCanonicalHandlesForTesting(
std::unique_ptr<PersistentHandles> persistent_handles, std::unique_ptr<PersistentHandles> persistent_handles,
...@@ -2471,21 +2479,22 @@ std::string JSHeapBroker::Trace() const { ...@@ -2471,21 +2479,22 @@ std::string JSHeapBroker::Trace() const {
return oss.str(); return oss.str();
} }
void JSHeapBroker::InitializeLocalHeap(OptimizedCompilationInfo* info, void JSHeapBroker::AttachLocalIsolate(OptimizedCompilationInfo* info,
LocalHeap* local_heap) { LocalIsolate* local_isolate) {
set_canonical_handles(info->DetachCanonicalHandles()); set_canonical_handles(info->DetachCanonicalHandles());
DCHECK_NULL(local_heap_); DCHECK_NULL(local_isolate_);
local_heap_ = local_heap; local_isolate_ = local_isolate;
DCHECK_NOT_NULL(local_heap_); DCHECK_NOT_NULL(local_isolate_);
local_heap_->AttachPersistentHandles(info->DetachPersistentHandles()); local_isolate_->heap()->AttachPersistentHandles(
info->DetachPersistentHandles());
} }
void JSHeapBroker::TearDownLocalHeap(OptimizedCompilationInfo* info) { void JSHeapBroker::DetachLocalIsolate(OptimizedCompilationInfo* info) {
DCHECK_NULL(ph_); DCHECK_NULL(ph_);
DCHECK(local_heap_); DCHECK_NOT_NULL(local_isolate_);
std::unique_ptr<PersistentHandles> ph = std::unique_ptr<PersistentHandles> ph =
local_heap_->DetachPersistentHandles(); local_isolate_->heap()->DetachPersistentHandles();
local_heap_ = nullptr; local_isolate_ = nullptr;
info->set_canonical_handles(DetachCanonicalHandles()); info->set_canonical_handles(DetachCanonicalHandles());
info->set_persistent_handles(std::move(ph)); info->set_persistent_handles(std::move(ph));
} }
...@@ -4357,10 +4366,10 @@ bool JSTypedArrayRef::serialized() const { ...@@ -4357,10 +4366,10 @@ bool JSTypedArrayRef::serialized() const {
return data()->AsJSTypedArray()->serialized(); return data()->AsJSTypedArray()->serialized();
} }
void JSBoundFunctionRef::Serialize() { bool JSBoundFunctionRef::Serialize() {
if (data_->should_access_heap()) return; if (data_->should_access_heap()) return true;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing); CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsJSBoundFunction()->Serialize(broker()); return data()->AsJSBoundFunction()->Serialize(broker());
} }
void PropertyCellRef::Serialize() { void PropertyCellRef::Serialize() {
...@@ -5331,6 +5340,14 @@ BytecodeAnalysis const& JSHeapBroker::GetBytecodeAnalysis( ...@@ -5331,6 +5340,14 @@ BytecodeAnalysis const& JSHeapBroker::GetBytecodeAnalysis(
return *analysis; return *analysis;
} }
bool JSHeapBroker::StackHasOverflowed() const {
DCHECK_IMPLIES(local_isolate_ == nullptr,
ThreadId::Current() == isolate_->thread_id());
return (local_isolate_ != nullptr)
? StackLimitCheck::HasOverflowed(local_isolate_)
: StackLimitCheck(isolate_).HasOverflowed();
}
OffHeapBytecodeArray::OffHeapBytecodeArray(BytecodeArrayRef bytecode_array) OffHeapBytecodeArray::OffHeapBytecodeArray(BytecodeArrayRef bytecode_array)
: array_(bytecode_array) {} : array_(bytecode_array) {}
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "src/compiler/processed-feedback.h" #include "src/compiler/processed-feedback.h"
#include "src/compiler/refs-map.h" #include "src/compiler/refs-map.h"
#include "src/compiler/serializer-hints.h" #include "src/compiler/serializer-hints.h"
#include "src/execution/local-isolate.h"
#include "src/handles/handles.h" #include "src/handles/handles.h"
#include "src/handles/persistent-handles.h" #include "src/handles/persistent-handles.h"
#include "src/heap/local-heap.h" #include "src/heap/local-heap.h"
...@@ -33,6 +34,7 @@ namespace compiler { ...@@ -33,6 +34,7 @@ namespace compiler {
class BytecodeAnalysis; class BytecodeAnalysis;
class ObjectRef; class ObjectRef;
std::ostream& operator<<(std::ostream& os, const ObjectRef& ref); std::ostream& operator<<(std::ostream& os, const ObjectRef& ref);
#define TRACE_BROKER(broker, x) \ #define TRACE_BROKER(broker, x) \
...@@ -115,17 +117,22 @@ class V8_EXPORT_PRIVATE JSHeapBroker { ...@@ -115,17 +117,22 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
enum BrokerMode { kDisabled, kSerializing, kSerialized, kRetired }; enum BrokerMode { kDisabled, kSerializing, kSerialized, kRetired };
BrokerMode mode() const { return mode_; } BrokerMode mode() const { return mode_; }
// Initialize the local heap with the persistent and canonical handles
// provided by {info}.
void InitializeLocalHeap(OptimizedCompilationInfo* info,
LocalHeap* local_heap);
// Tear down the local heap and pass the persistent and canonical handles
// provided back to {info}. {info} is responsible for disposing of them.
void TearDownLocalHeap(OptimizedCompilationInfo* info);
void StopSerializing(); void StopSerializing();
void Retire(); void Retire();
bool SerializingAllowed() const; bool SerializingAllowed() const;
// Remember the local isolate and initialize its local heap with the
// persistent and canonical handles provided by {info}.
void AttachLocalIsolate(OptimizedCompilationInfo* info,
LocalIsolate* local_isolate);
// Forget about the local isolate and pass the persistent and canonical
// handles provided back to {info}. {info} is responsible for disposing of
// them.
void DetachLocalIsolate(OptimizedCompilationInfo* info);
bool StackHasOverflowed() const;
#ifdef DEBUG #ifdef DEBUG
void PrintRefsAnalysis() const; void PrintRefsAnalysis() const;
#endif // DEBUG #endif // DEBUG
...@@ -228,7 +235,7 @@ class V8_EXPORT_PRIVATE JSHeapBroker { ...@@ -228,7 +235,7 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
bool IsSerializedForCompilation(const SharedFunctionInfoRef& shared, bool IsSerializedForCompilation(const SharedFunctionInfoRef& shared,
const FeedbackVectorRef& feedback) const; const FeedbackVectorRef& feedback) const;
LocalHeap* local_heap() { return local_heap_; } LocalIsolate* local_isolate() const { return local_isolate_; }
// Return the corresponding canonical persistent handle for {object}. Create // Return the corresponding canonical persistent handle for {object}. Create
// one if it does not exist. // one if it does not exist.
...@@ -252,8 +259,9 @@ class V8_EXPORT_PRIVATE JSHeapBroker { ...@@ -252,8 +259,9 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
auto find_result = canonical_handles_->FindOrInsert(obj); auto find_result = canonical_handles_->FindOrInsert(obj);
if (!find_result.already_exists) { if (!find_result.already_exists) {
// Allocate new PersistentHandle if one wasn't created before. // Allocate new PersistentHandle if one wasn't created before.
DCHECK(local_heap_); DCHECK_NOT_NULL(local_isolate());
*find_result.entry = local_heap_->NewPersistentHandle(obj).location(); *find_result.entry =
local_isolate()->heap()->NewPersistentHandle(obj).location();
} }
return Handle<T>(*find_result.entry); return Handle<T>(*find_result.entry);
} else { } else {
...@@ -360,7 +368,7 @@ class V8_EXPORT_PRIVATE JSHeapBroker { ...@@ -360,7 +368,7 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
bool const is_concurrent_inlining_; bool const is_concurrent_inlining_;
CodeKind const code_kind_; CodeKind const code_kind_;
std::unique_ptr<PersistentHandles> ph_; std::unique_ptr<PersistentHandles> ph_;
LocalHeap* local_heap_ = nullptr; LocalIsolate* local_isolate_ = nullptr;
std::unique_ptr<CanonicalHandlesMap> canonical_handles_; std::unique_ptr<CanonicalHandlesMap> canonical_handles_;
unsigned trace_indentation_ = 0; unsigned trace_indentation_ = 0;
PerIsolateCompilerCache* compiler_cache_ = nullptr; PerIsolateCompilerCache* compiler_cache_ = nullptr;
...@@ -452,7 +460,7 @@ class OffHeapBytecodeArray final : public interpreter::AbstractBytecodeArray { ...@@ -452,7 +460,7 @@ class OffHeapBytecodeArray final : public interpreter::AbstractBytecodeArray {
// Scope that unparks the LocalHeap, if: // Scope that unparks the LocalHeap, if:
// a) We have a JSHeapBroker, // a) We have a JSHeapBroker,
// b) Said JSHeapBroker has a LocalHeap, // b) Said JSHeapBroker has a LocalIsolate and thus a LocalHeap,
// c) Said LocalHeap has been parked and // c) Said LocalHeap has been parked and
// d) The given condition evaluates to true. // d) The given condition evaluates to true.
// Used, for example, when printing the graph with --trace-turbo with a // Used, for example, when printing the graph with --trace-turbo with a
...@@ -462,9 +470,9 @@ class UnparkedScopeIfNeeded { ...@@ -462,9 +470,9 @@ class UnparkedScopeIfNeeded {
explicit UnparkedScopeIfNeeded(JSHeapBroker* broker, explicit UnparkedScopeIfNeeded(JSHeapBroker* broker,
bool extra_condition = true) { bool extra_condition = true) {
if (broker != nullptr && extra_condition) { if (broker != nullptr && extra_condition) {
LocalHeap* local_heap = broker->local_heap(); LocalIsolate* local_isolate = broker->local_isolate();
if (local_heap != nullptr && local_heap->IsParked()) { if (local_isolate != nullptr && local_isolate->heap()->IsParked()) {
unparked_scope.emplace(local_heap); unparked_scope.emplace(local_isolate->heap());
} }
} }
} }
......
...@@ -766,20 +766,21 @@ class PipelineRunScope { ...@@ -766,20 +766,21 @@ class PipelineRunScope {
RuntimeCallTimerScope runtime_call_timer_scope; RuntimeCallTimerScope runtime_call_timer_scope;
}; };
// LocalHeapScope encapsulates the phase where persistent handles are attached // LocalIsolateScope encapsulates the phase where persistent handles are
// to the LocalHeap. // attached to the LocalHeap inside {local_isolate}.
class LocalHeapScope { class LocalIsolateScope {
public: public:
explicit LocalHeapScope(JSHeapBroker* broker, OptimizedCompilationInfo* info, explicit LocalIsolateScope(JSHeapBroker* broker,
LocalHeap* local_heap) OptimizedCompilationInfo* info,
LocalIsolate* local_isolate)
: broker_(broker), info_(info) { : broker_(broker), info_(info) {
broker_->InitializeLocalHeap(info_, local_heap); broker_->AttachLocalIsolate(info_, local_isolate);
info_->tick_counter().AttachLocalHeap(broker_->local_heap()); info_->tick_counter().AttachLocalHeap(local_isolate->heap());
} }
~LocalHeapScope() { ~LocalIsolateScope() {
info_->tick_counter().DetachLocalHeap(); info_->tick_counter().DetachLocalHeap();
broker_->TearDownLocalHeap(info_); broker_->DetachLocalIsolate(info_);
} }
private: private:
...@@ -1185,8 +1186,8 @@ PipelineCompilationJob::Status PipelineCompilationJob::ExecuteJobImpl( ...@@ -1185,8 +1186,8 @@ PipelineCompilationJob::Status PipelineCompilationJob::ExecuteJobImpl(
// Ensure that the RuntimeCallStats table is only available during execution // Ensure that the RuntimeCallStats table is only available during execution
// and not during finalization as that might be on a different thread. // and not during finalization as that might be on a different thread.
PipelineJobScope scope(&data_, stats); PipelineJobScope scope(&data_, stats);
LocalHeapScope local_heap_scope(data_.broker(), data_.info(), LocalIsolateScope local_isolate_scope(data_.broker(), data_.info(),
local_isolate->heap()); local_isolate);
if (data_.broker()->is_concurrent_inlining()) { if (data_.broker()->is_concurrent_inlining()) {
if (!pipeline_.CreateGraph()) { if (!pipeline_.CreateGraph()) {
...@@ -3018,7 +3019,7 @@ MaybeHandle<Code> Pipeline::GenerateCodeForTesting( ...@@ -3018,7 +3019,7 @@ MaybeHandle<Code> Pipeline::GenerateCodeForTesting(
info->ReopenHandlesInNewHandleScope(isolate); info->ReopenHandlesInNewHandleScope(isolate);
pipeline.Serialize(); pipeline.Serialize();
// Emulating the proper pipeline, we call CreateGraph on different places // Emulating the proper pipeline, we call CreateGraph on different places
// (i.e before or after creating a LocalHeapScope) depending on // (i.e before or after creating a LocalIsolateScope) depending on
// is_concurrent_inlining. // is_concurrent_inlining.
if (!data.broker()->is_concurrent_inlining()) { if (!data.broker()->is_concurrent_inlining()) {
if (!pipeline.CreateGraph()) return MaybeHandle<Code>(); if (!pipeline.CreateGraph()) return MaybeHandle<Code>();
...@@ -3027,7 +3028,7 @@ MaybeHandle<Code> Pipeline::GenerateCodeForTesting( ...@@ -3027,7 +3028,7 @@ MaybeHandle<Code> Pipeline::GenerateCodeForTesting(
{ {
LocalIsolate local_isolate(isolate, ThreadKind::kMain); LocalIsolate local_isolate(isolate, ThreadKind::kMain);
LocalHeapScope local_heap_scope(data.broker(), info, local_isolate.heap()); LocalIsolateScope local_isolate_scope(data.broker(), info, &local_isolate);
if (data.broker()->is_concurrent_inlining()) { if (data.broker()->is_concurrent_inlining()) {
if (!pipeline.CreateGraph()) return MaybeHandle<Code>(); if (!pipeline.CreateGraph()) return MaybeHandle<Code>();
} }
......
...@@ -2084,7 +2084,7 @@ void SerializerForBackgroundCompilation::ProcessCalleeForCallOrConstruct( ...@@ -2084,7 +2084,7 @@ void SerializerForBackgroundCompilation::ProcessCalleeForCallOrConstruct(
if (callee->IsJSBoundFunction()) { if (callee->IsJSBoundFunction()) {
JSBoundFunctionRef bound_function(broker(), JSBoundFunctionRef bound_function(broker(),
Handle<JSBoundFunction>::cast(callee)); Handle<JSBoundFunction>::cast(callee));
bound_function.Serialize(); if (!bound_function.Serialize()) return;
callee = UnrollBoundFunction(bound_function, broker(), arguments, callee = UnrollBoundFunction(bound_function, broker(), arguments,
&expanded_arguments, zone()) &expanded_arguments, zone())
.object(); .object();
......
// Copyright 2020 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 --opt
function foo() {}
for (let i = 0; i < 100000; ++i) {
foo = foo.bind();
}
function main() {
foo();
}
%PrepareFunctionForOptimization(main);
main();
%OptimizeFunctionOnNextCall(main);
main();
...@@ -191,6 +191,7 @@ ...@@ -191,6 +191,7 @@
# Skip slow tests in debug mode. # Skip slow tests in debug mode.
'array-functions-prototype-misc': [SKIP], 'array-functions-prototype-misc': [SKIP],
'compiler/regress-808472': [SKIP], 'compiler/regress-808472': [SKIP],
'compiler/regress-1125145': [SKIP],
'es6/promise-all-overflow-2': [SKIP], 'es6/promise-all-overflow-2': [SKIP],
'generated-transition-stub': [SKIP], 'generated-transition-stub': [SKIP],
'regress/regress-524': [SKIP], 'regress/regress-524': [SKIP],
...@@ -627,6 +628,7 @@ ...@@ -627,6 +628,7 @@
'es6/large-classes-properties': [SKIP], 'es6/large-classes-properties': [SKIP],
# Slow tests. # Slow tests.
'compiler/regress-1125145': [SKIP],
'es6/block-conflicts-sloppy': [PASS, SLOW], 'es6/block-conflicts-sloppy': [PASS, SLOW],
'math-floor-part1': [PASS, SLOW], 'math-floor-part1': [PASS, SLOW],
'regress/regress-430201': [SKIP], 'regress/regress-430201': [SKIP],
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment