Commit bba36e19 authored by Jaroslav Sevcik's avatar Jaroslav Sevcik Committed by Commit Bot

[turbofan] Optimistic slack tracking completion.

The idea is to compute the slack before compilation start. Then
we check that the slack tracking decision is the same at the end
of compilation. If it is, we just commit to that slack tracking
(by calling function->CompleteInobjectSlackTrackingIfActive).
If the slack tracking decision changed, we will retry the compilation.

This has several pieces:
- Expose computation of slack and instance size from the object model.
- Add compilation dependency on the slack tracking result.
- Change create lowering to use the dependency.
- Fix array creation to use the slack tracking result's instance
  size.

Bug: v8:7790
Change-Id: Id975300cfd6c1786733cd7cbf55cc507c05738b2
Reviewed-on: https://chromium-review.googlesource.com/1164957Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Commit-Queue: Jaroslav Sevcik <jarin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54982}
parent 0a719469
...@@ -17,7 +17,7 @@ CompilationDependencies::CompilationDependencies(Isolate* isolate, Zone* zone) ...@@ -17,7 +17,7 @@ CompilationDependencies::CompilationDependencies(Isolate* isolate, Zone* zone)
class CompilationDependencies::Dependency : public ZoneObject { class CompilationDependencies::Dependency : public ZoneObject {
public: public:
virtual bool IsValid() const = 0; virtual bool IsValid() const = 0;
virtual void Install(Isolate* isolate, MaybeObjectHandle code) = 0; virtual void Install(MaybeObjectHandle code) = 0;
}; };
class InitialMapDependency final : public CompilationDependencies::Dependency { class InitialMapDependency final : public CompilationDependencies::Dependency {
...@@ -36,9 +36,10 @@ class InitialMapDependency final : public CompilationDependencies::Dependency { ...@@ -36,9 +36,10 @@ class InitialMapDependency final : public CompilationDependencies::Dependency {
function->initial_map() == *initial_map_.object<Map>(); function->initial_map() == *initial_map_.object<Map>();
} }
void Install(Isolate* isolate, MaybeObjectHandle code) override { void Install(MaybeObjectHandle code) override {
SLOW_DCHECK(IsValid()); SLOW_DCHECK(IsValid());
DependentCode::InstallDependency(isolate, code, initial_map_.object<Map>(), DependentCode::InstallDependency(function_.isolate(), code,
initial_map_.object<Map>(),
DependentCode::kInitialMapChangedGroup); DependentCode::kInitialMapChangedGroup);
} }
...@@ -55,9 +56,9 @@ class StableMapDependency final : public CompilationDependencies::Dependency { ...@@ -55,9 +56,9 @@ class StableMapDependency final : public CompilationDependencies::Dependency {
bool IsValid() const override { return map_.object<Map>()->is_stable(); } bool IsValid() const override { return map_.object<Map>()->is_stable(); }
void Install(Isolate* isolate, MaybeObjectHandle code) override { void Install(MaybeObjectHandle code) override {
SLOW_DCHECK(IsValid()); SLOW_DCHECK(IsValid());
DependentCode::InstallDependency(isolate, code, map_.object<Map>(), DependentCode::InstallDependency(map_.isolate(), code, map_.object<Map>(),
DependentCode::kPrototypeCheckGroup); DependentCode::kPrototypeCheckGroup);
} }
...@@ -73,9 +74,9 @@ class TransitionDependency final : public CompilationDependencies::Dependency { ...@@ -73,9 +74,9 @@ class TransitionDependency final : public CompilationDependencies::Dependency {
bool IsValid() const override { return !map_.object<Map>()->is_deprecated(); } bool IsValid() const override { return !map_.object<Map>()->is_deprecated(); }
void Install(Isolate* isolate, MaybeObjectHandle code) override { void Install(MaybeObjectHandle code) override {
SLOW_DCHECK(IsValid()); SLOW_DCHECK(IsValid());
DependentCode::InstallDependency(isolate, code, map_.object<Map>(), DependentCode::InstallDependency(map_.isolate(), code, map_.object<Map>(),
DependentCode::kTransitionGroup); DependentCode::kTransitionGroup);
} }
...@@ -97,10 +98,10 @@ class PretenureModeDependency final ...@@ -97,10 +98,10 @@ class PretenureModeDependency final
return mode_ == site_.object<AllocationSite>()->GetPretenureMode(); return mode_ == site_.object<AllocationSite>()->GetPretenureMode();
} }
void Install(Isolate* isolate, MaybeObjectHandle code) override { void Install(MaybeObjectHandle code) override {
SLOW_DCHECK(IsValid()); SLOW_DCHECK(IsValid());
DependentCode::InstallDependency( DependentCode::InstallDependency(
isolate, code, site_.object<AllocationSite>(), site_.isolate(), code, site_.object<AllocationSite>(),
DependentCode::kAllocationSiteTenuringChangedGroup); DependentCode::kAllocationSiteTenuringChangedGroup);
} }
...@@ -127,9 +128,10 @@ class FieldTypeDependency final : public CompilationDependencies::Dependency { ...@@ -127,9 +128,10 @@ class FieldTypeDependency final : public CompilationDependencies::Dependency {
return *type == owner->instance_descriptors()->GetFieldType(descriptor_); return *type == owner->instance_descriptors()->GetFieldType(descriptor_);
} }
void Install(Isolate* isolate, MaybeObjectHandle code) override { void Install(MaybeObjectHandle code) override {
SLOW_DCHECK(IsValid()); SLOW_DCHECK(IsValid());
DependentCode::InstallDependency(isolate, code, owner_.object<Map>(), DependentCode::InstallDependency(owner_.isolate(), code,
owner_.object<Map>(),
DependentCode::kFieldOwnerGroup); DependentCode::kFieldOwnerGroup);
} }
...@@ -157,9 +159,9 @@ class GlobalPropertyDependency final ...@@ -157,9 +159,9 @@ class GlobalPropertyDependency final
read_only_ == cell->property_details().IsReadOnly(); read_only_ == cell->property_details().IsReadOnly();
} }
void Install(Isolate* isolate, MaybeObjectHandle code) override { void Install(MaybeObjectHandle code) override {
SLOW_DCHECK(IsValid()); SLOW_DCHECK(IsValid());
DependentCode::InstallDependency(isolate, code, DependentCode::InstallDependency(cell_.isolate(), code,
cell_.object<PropertyCell>(), cell_.object<PropertyCell>(),
DependentCode::kPropertyCellChangedGroup); DependentCode::kPropertyCellChangedGroup);
} }
...@@ -181,9 +183,9 @@ class ProtectorDependency final : public CompilationDependencies::Dependency { ...@@ -181,9 +183,9 @@ class ProtectorDependency final : public CompilationDependencies::Dependency {
return cell->value() == Smi::FromInt(Isolate::kProtectorValid); return cell->value() == Smi::FromInt(Isolate::kProtectorValid);
} }
void Install(Isolate* isolate, MaybeObjectHandle code) override { void Install(MaybeObjectHandle code) override {
SLOW_DCHECK(IsValid()); SLOW_DCHECK(IsValid());
DependentCode::InstallDependency(isolate, code, DependentCode::InstallDependency(cell_.isolate(), code,
cell_.object<PropertyCell>(), cell_.object<PropertyCell>(),
DependentCode::kPropertyCellChangedGroup); DependentCode::kPropertyCellChangedGroup);
} }
...@@ -213,10 +215,10 @@ class ElementsKindDependency final ...@@ -213,10 +215,10 @@ class ElementsKindDependency final
return kind_ == kind; return kind_ == kind;
} }
void Install(Isolate* isolate, MaybeObjectHandle code) override { void Install(MaybeObjectHandle code) override {
SLOW_DCHECK(IsValid()); SLOW_DCHECK(IsValid());
DependentCode::InstallDependency( DependentCode::InstallDependency(
isolate, code, site_.object<AllocationSite>(), site_.isolate(), code, site_.object<AllocationSite>(),
DependentCode::kAllocationSiteTransitionChangedGroup); DependentCode::kAllocationSiteTransitionChangedGroup);
} }
...@@ -225,6 +227,33 @@ class ElementsKindDependency final ...@@ -225,6 +227,33 @@ class ElementsKindDependency final
ElementsKind kind_; ElementsKind kind_;
}; };
class InitialMapInstanceSizePredictionDependency final
: public CompilationDependencies::Dependency {
public:
InitialMapInstanceSizePredictionDependency(const JSFunctionRef& function,
int instance_size)
: function_(function), instance_size_(instance_size) {}
bool IsValid() const override {
// The dependency is valid if the prediction is the same as the current
// slack tracking result.
int instance_size =
function_.object<JSFunction>()->ComputeInstanceSizeWithMinSlack(
function_.isolate());
return instance_size == instance_size_;
}
void Install(MaybeObjectHandle code) override {
DCHECK(IsValid());
// Finish the slack tracking.
function_.object<JSFunction>()->CompleteInobjectSlackTrackingIfActive();
}
private:
JSFunctionRef function_;
int instance_size_;
};
MapRef CompilationDependencies::DependOnInitialMap( MapRef CompilationDependencies::DependOnInitialMap(
const JSFunctionRef& function) { const JSFunctionRef& function) {
MapRef map = function.initial_map(); MapRef map = function.initial_map();
...@@ -295,8 +324,6 @@ bool CompilationDependencies::AreValid() const { ...@@ -295,8 +324,6 @@ bool CompilationDependencies::AreValid() const {
} }
bool CompilationDependencies::Commit(Handle<Code> code) { bool CompilationDependencies::Commit(Handle<Code> code) {
Isolate* isolate = code->GetIsolate();
// Check validity of all dependencies first, such that we can avoid installing // Check validity of all dependencies first, such that we can avoid installing
// anything when there's already an invalid dependency. // anything when there's already an invalid dependency.
if (!AreValid()) { if (!AreValid()) {
...@@ -311,7 +338,7 @@ bool CompilationDependencies::Commit(Handle<Code> code) { ...@@ -311,7 +338,7 @@ bool CompilationDependencies::Commit(Handle<Code> code) {
dependencies_.clear(); dependencies_.clear();
return false; return false;
} }
dep->Install(isolate, MaybeObjectHandle::Weak(code)); dep->Install(MaybeObjectHandle::Weak(code));
} }
dependencies_.clear(); dependencies_.clear();
return true; return true;
...@@ -363,6 +390,28 @@ void CompilationDependencies::DependOnElementsKinds( ...@@ -363,6 +390,28 @@ void CompilationDependencies::DependOnElementsKinds(
CHECK_EQ(current.nested_site().AsSmi(), 0); CHECK_EQ(current.nested_site().AsSmi(), 0);
} }
SlackTrackingPrediction::SlackTrackingPrediction(MapRef initial_map,
int instance_size)
: instance_size_(instance_size),
inobject_property_count_(
(instance_size >> kPointerSizeLog2) -
initial_map.GetInObjectPropertiesStartInWords()) {}
SlackTrackingPrediction
CompilationDependencies::DependOnInitialMapInstanceSizePrediction(
const JSFunctionRef& function) {
MapRef initial_map = DependOnInitialMap(function);
int instance_size = function.InitialMapInstanceSizeWithMinSlack();
// Currently, we always install the prediction dependency. If this turns out
// to be too expensive, we can only install the dependency if slack
// tracking is active.
dependencies_.push_front(
new (zone_)
InitialMapInstanceSizePredictionDependency(function, instance_size));
DCHECK_LE(instance_size, function.initial_map().instance_size());
return SlackTrackingPrediction(initial_map, instance_size);
}
} // namespace compiler } // namespace compiler
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -13,6 +13,18 @@ namespace v8 { ...@@ -13,6 +13,18 @@ namespace v8 {
namespace internal { namespace internal {
namespace compiler { namespace compiler {
class SlackTrackingPrediction {
public:
SlackTrackingPrediction(MapRef initial_map, int instance_size);
int inobject_property_count() const { return inobject_property_count_; }
int instance_size() const { return instance_size_; }
private:
int instance_size_;
int inobject_property_count_;
};
// Collects and installs dependencies of the code that is being generated. // Collects and installs dependencies of the code that is being generated.
class V8_EXPORT_PRIVATE CompilationDependencies : public ZoneObject { class V8_EXPORT_PRIVATE CompilationDependencies : public ZoneObject {
public: public:
...@@ -21,7 +33,7 @@ class V8_EXPORT_PRIVATE CompilationDependencies : public ZoneObject { ...@@ -21,7 +33,7 @@ class V8_EXPORT_PRIVATE CompilationDependencies : public ZoneObject {
V8_WARN_UNUSED_RESULT bool Commit(Handle<Code> code); V8_WARN_UNUSED_RESULT bool Commit(Handle<Code> code);
// Return the initial map of {function} and record the assumption that it // Return the initial map of {function} and record the assumption that it
// stays the intial map. // stays the initial map.
MapRef DependOnInitialMap(const JSFunctionRef& function); MapRef DependOnInitialMap(const JSFunctionRef& function);
// Record the assumption that {map} stays stable. // Record the assumption that {map} stays stable.
...@@ -59,6 +71,14 @@ class V8_EXPORT_PRIVATE CompilationDependencies : public ZoneObject { ...@@ -59,6 +71,14 @@ class V8_EXPORT_PRIVATE CompilationDependencies : public ZoneObject {
// Like DependOnElementsKind but also applies to all nested allocation sites. // Like DependOnElementsKind but also applies to all nested allocation sites.
void DependOnElementsKinds(const AllocationSiteRef& site); void DependOnElementsKinds(const AllocationSiteRef& site);
// Predict the final instance size for {function}'s initial map and record
// the assumption that this prediction is correct. In addition, register
// the initial map dependency. This method returns the {function}'s the
// predicted minimum slack instance size count (wrapped together with
// the corresponding in-object property count for convenience).
SlackTrackingPrediction DependOnInitialMapInstanceSizePrediction(
const JSFunctionRef& function);
// Exposed only for testing purposes. // Exposed only for testing purposes.
bool AreValid() const; bool AreValid() const;
......
...@@ -138,25 +138,22 @@ Reduction JSCreateLowering::ReduceJSCreate(Node* node) { ...@@ -138,25 +138,22 @@ Reduction JSCreateLowering::ReduceJSCreate(Node* node) {
return NoChange(); return NoChange();
} }
// Add a dependency on the {initial_map} to make sure that this code is SlackTrackingPrediction slack_tracking_prediction =
// deoptimized whenever the {initial_map} changes. dependencies()->DependOnInitialMapInstanceSizePrediction(
MapRef initial_map = dependencies()->DependOnInitialMap(original_constructor); original_constructor);
MapRef initial_map = original_constructor.initial_map();
// Force completion of inobject slack tracking before
// generating code to finalize the instance size.
SlackTrackingResult slack_tracking_result =
original_constructor.FinishSlackTracking();
// Emit code to allocate the JSObject instance for the // Emit code to allocate the JSObject instance for the
// {original_constructor}. // {original_constructor}.
AllocationBuilder a(jsgraph(), effect, control); AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(slack_tracking_result.instance_size); a.Allocate(slack_tracking_prediction.instance_size());
a.Store(AccessBuilder::ForMap(), initial_map); a.Store(AccessBuilder::ForMap(), initial_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(),
jsgraph()->EmptyFixedArrayConstant()); jsgraph()->EmptyFixedArrayConstant());
a.Store(AccessBuilder::ForJSObjectElements(), a.Store(AccessBuilder::ForJSObjectElements(),
jsgraph()->EmptyFixedArrayConstant()); jsgraph()->EmptyFixedArrayConstant());
for (int i = 0; i < slack_tracking_result.inobject_property_count; ++i) { for (int i = 0; i < slack_tracking_prediction.inobject_property_count();
++i) {
a.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i), a.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i),
jsgraph()->UndefinedConstant()); jsgraph()->UndefinedConstant());
} }
...@@ -417,14 +414,10 @@ Reduction JSCreateLowering::ReduceJSCreateGeneratorObject(Node* node) { ...@@ -417,14 +414,10 @@ Reduction JSCreateLowering::ReduceJSCreateGeneratorObject(Node* node) {
closure_type.AsHeapConstant()->Ref().AsJSFunction(); closure_type.AsHeapConstant()->Ref().AsJSFunction();
js_function.EnsureHasInitialMap(); js_function.EnsureHasInitialMap();
// Force completion of inobject slack tracking before SlackTrackingPrediction slack_tracking_prediction =
// generating code to finalize the instance size. dependencies()->DependOnInitialMapInstanceSizePrediction(js_function);
SlackTrackingResult slack_tracking_result =
js_function.FinishSlackTracking();
// Add a dependency on the {initial_map} to make sure that this code is MapRef initial_map = js_function.initial_map();
// deoptimized whenever the {initial_map} changes.
MapRef initial_map = dependencies()->DependOnInitialMap(js_function);
DCHECK(initial_map.instance_type() == JS_GENERATOR_OBJECT_TYPE || DCHECK(initial_map.instance_type() == JS_GENERATOR_OBJECT_TYPE ||
initial_map.instance_type() == JS_ASYNC_GENERATOR_OBJECT_TYPE); initial_map.instance_type() == JS_ASYNC_GENERATOR_OBJECT_TYPE);
...@@ -444,7 +437,7 @@ Reduction JSCreateLowering::ReduceJSCreateGeneratorObject(Node* node) { ...@@ -444,7 +437,7 @@ Reduction JSCreateLowering::ReduceJSCreateGeneratorObject(Node* node) {
// Emit code to allocate the JS[Async]GeneratorObject instance. // Emit code to allocate the JS[Async]GeneratorObject instance.
AllocationBuilder a(jsgraph(), effect, control); AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(slack_tracking_result.instance_size); a.Allocate(slack_tracking_prediction.instance_size());
Node* empty_fixed_array = jsgraph()->EmptyFixedArrayConstant(); Node* empty_fixed_array = jsgraph()->EmptyFixedArrayConstant();
Node* undefined = jsgraph()->UndefinedConstant(); Node* undefined = jsgraph()->UndefinedConstant();
a.Store(AccessBuilder::ForMap(), initial_map); a.Store(AccessBuilder::ForMap(), initial_map);
...@@ -468,7 +461,8 @@ Reduction JSCreateLowering::ReduceJSCreateGeneratorObject(Node* node) { ...@@ -468,7 +461,8 @@ Reduction JSCreateLowering::ReduceJSCreateGeneratorObject(Node* node) {
} }
// Handle in-object properties, too. // Handle in-object properties, too.
for (int i = 0; i < slack_tracking_result.inobject_property_count; ++i) { for (int i = 0; i < slack_tracking_prediction.inobject_property_count();
++i) {
a.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i), a.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i),
undefined); undefined);
} }
...@@ -480,9 +474,9 @@ Reduction JSCreateLowering::ReduceJSCreateGeneratorObject(Node* node) { ...@@ -480,9 +474,9 @@ Reduction JSCreateLowering::ReduceJSCreateGeneratorObject(Node* node) {
// Constructs an array with a variable {length} when no upper bound // Constructs an array with a variable {length} when no upper bound
// is known for the capacity. // is known for the capacity.
Reduction JSCreateLowering::ReduceNewArray(Node* node, Node* length, Reduction JSCreateLowering::ReduceNewArray(
MapRef initial_map, Node* node, Node* length, MapRef initial_map, PretenureFlag pretenure,
PretenureFlag pretenure) { const SlackTrackingPrediction& slack_tracking_prediction) {
DCHECK_EQ(IrOpcode::kJSCreateArray, node->opcode()); DCHECK_EQ(IrOpcode::kJSCreateArray, node->opcode());
Node* effect = NodeProperties::GetEffectInput(node); Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node); Node* control = NodeProperties::GetControlInput(node);
...@@ -510,12 +504,13 @@ Reduction JSCreateLowering::ReduceNewArray(Node* node, Node* length, ...@@ -510,12 +504,13 @@ Reduction JSCreateLowering::ReduceNewArray(Node* node, Node* length,
// Perform the allocation of the actual JSArray object. // Perform the allocation of the actual JSArray object.
AllocationBuilder a(jsgraph(), effect, control); AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(initial_map.instance_size(), pretenure); a.Allocate(slack_tracking_prediction.instance_size(), pretenure);
a.Store(AccessBuilder::ForMap(), initial_map); a.Store(AccessBuilder::ForMap(), initial_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties); a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties);
a.Store(AccessBuilder::ForJSObjectElements(), elements); a.Store(AccessBuilder::ForJSObjectElements(), elements);
a.Store(AccessBuilder::ForJSArrayLength(initial_map.elements_kind()), length); a.Store(AccessBuilder::ForJSArrayLength(initial_map.elements_kind()), length);
for (int i = 0; i < initial_map.GetInObjectProperties(); ++i) { for (int i = 0; i < slack_tracking_prediction.inobject_property_count();
++i) {
a.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i), a.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i),
jsgraph()->UndefinedConstant()); jsgraph()->UndefinedConstant());
} }
...@@ -526,9 +521,10 @@ Reduction JSCreateLowering::ReduceNewArray(Node* node, Node* length, ...@@ -526,9 +521,10 @@ Reduction JSCreateLowering::ReduceNewArray(Node* node, Node* length,
// Constructs an array with a variable {length} when an actual // Constructs an array with a variable {length} when an actual
// upper bound is known for the {capacity}. // upper bound is known for the {capacity}.
Reduction JSCreateLowering::ReduceNewArray(Node* node, Node* length, Reduction JSCreateLowering::ReduceNewArray(
int capacity, MapRef initial_map, Node* node, Node* length, int capacity, MapRef initial_map,
PretenureFlag pretenure) { PretenureFlag pretenure,
const SlackTrackingPrediction& slack_tracking_prediction) {
DCHECK(node->opcode() == IrOpcode::kJSCreateArray || DCHECK(node->opcode() == IrOpcode::kJSCreateArray ||
node->opcode() == IrOpcode::kJSCreateEmptyLiteralArray); node->opcode() == IrOpcode::kJSCreateEmptyLiteralArray);
Node* effect = NodeProperties::GetEffectInput(node); Node* effect = NodeProperties::GetEffectInput(node);
...@@ -554,12 +550,13 @@ Reduction JSCreateLowering::ReduceNewArray(Node* node, Node* length, ...@@ -554,12 +550,13 @@ Reduction JSCreateLowering::ReduceNewArray(Node* node, Node* length,
// Perform the allocation of the actual JSArray object. // Perform the allocation of the actual JSArray object.
AllocationBuilder a(jsgraph(), effect, control); AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(initial_map.instance_size(), pretenure); a.Allocate(slack_tracking_prediction.instance_size(), pretenure);
a.Store(AccessBuilder::ForMap(), initial_map); a.Store(AccessBuilder::ForMap(), initial_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties); a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties);
a.Store(AccessBuilder::ForJSObjectElements(), elements); a.Store(AccessBuilder::ForJSObjectElements(), elements);
a.Store(AccessBuilder::ForJSArrayLength(elements_kind), length); a.Store(AccessBuilder::ForJSArrayLength(elements_kind), length);
for (int i = 0; i < initial_map.GetInObjectProperties(); ++i) { for (int i = 0; i < slack_tracking_prediction.inobject_property_count();
++i) {
a.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i), a.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i),
jsgraph()->UndefinedConstant()); jsgraph()->UndefinedConstant());
} }
...@@ -568,10 +565,10 @@ Reduction JSCreateLowering::ReduceNewArray(Node* node, Node* length, ...@@ -568,10 +565,10 @@ Reduction JSCreateLowering::ReduceNewArray(Node* node, Node* length,
return Changed(node); return Changed(node);
} }
Reduction JSCreateLowering::ReduceNewArray(Node* node, Reduction JSCreateLowering::ReduceNewArray(
std::vector<Node*> values, Node* node, std::vector<Node*> values, MapRef initial_map,
MapRef initial_map, PretenureFlag pretenure,
PretenureFlag pretenure) { const SlackTrackingPrediction& slack_tracking_prediction) {
DCHECK_EQ(IrOpcode::kJSCreateArray, node->opcode()); DCHECK_EQ(IrOpcode::kJSCreateArray, node->opcode());
Node* effect = NodeProperties::GetEffectInput(node); Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node); Node* control = NodeProperties::GetControlInput(node);
...@@ -610,12 +607,13 @@ Reduction JSCreateLowering::ReduceNewArray(Node* node, ...@@ -610,12 +607,13 @@ Reduction JSCreateLowering::ReduceNewArray(Node* node,
// Perform the allocation of the actual JSArray object. // Perform the allocation of the actual JSArray object.
AllocationBuilder a(jsgraph(), effect, control); AllocationBuilder a(jsgraph(), effect, control);
a.Allocate(initial_map.instance_size(), pretenure); a.Allocate(slack_tracking_prediction.instance_size(), pretenure);
a.Store(AccessBuilder::ForMap(), initial_map); a.Store(AccessBuilder::ForMap(), initial_map);
a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties); a.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), properties);
a.Store(AccessBuilder::ForJSObjectElements(), elements); a.Store(AccessBuilder::ForJSObjectElements(), elements);
a.Store(AccessBuilder::ForJSArrayLength(elements_kind), length); a.Store(AccessBuilder::ForJSArrayLength(elements_kind), length);
for (int i = 0; i < initial_map.GetInObjectProperties(); ++i) { for (int i = 0; i < slack_tracking_prediction.inobject_property_count();
++i) {
a.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i), a.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i),
jsgraph()->UndefinedConstant()); jsgraph()->UndefinedConstant());
} }
...@@ -715,14 +713,10 @@ Reduction JSCreateLowering::ReduceJSCreateArray(Node* node) { ...@@ -715,14 +713,10 @@ Reduction JSCreateLowering::ReduceJSCreateArray(Node* node) {
// Check if we can inline the allocation. // Check if we can inline the allocation.
if (IsAllocationInlineable(constructor, original_constructor)) { if (IsAllocationInlineable(constructor, original_constructor)) {
// Force completion of inobject slack tracking before SlackTrackingPrediction slack_tracking_prediction =
// generating code to finalize the instance size. dependencies()->DependOnInitialMapInstanceSizePrediction(
original_constructor.FinishSlackTracking(); original_constructor);
MapRef initial_map = original_constructor.initial_map();
// Add a dependency on the {initial_map} to make sure that this code is
// deoptimized whenever the {initial_map} changes.
MapRef initial_map =
dependencies()->DependOnInitialMap(original_constructor);
// Tells whether we are protected by either the {site} or a // Tells whether we are protected by either the {site} or a
// protector cell to do certain speculative optimizations. // protector cell to do certain speculative optimizations.
...@@ -742,7 +736,8 @@ Reduction JSCreateLowering::ReduceJSCreateArray(Node* node) { ...@@ -742,7 +736,8 @@ Reduction JSCreateLowering::ReduceJSCreateArray(Node* node) {
if (arity == 0) { if (arity == 0) {
Node* length = jsgraph()->ZeroConstant(); Node* length = jsgraph()->ZeroConstant();
int capacity = JSArray::kPreallocatedArrayElements; int capacity = JSArray::kPreallocatedArrayElements;
return ReduceNewArray(node, length, capacity, initial_map, pretenure); return ReduceNewArray(node, length, capacity, initial_map, pretenure,
slack_tracking_prediction);
} else if (arity == 1) { } else if (arity == 1) {
Node* length = NodeProperties::GetValueInput(node, 2); Node* length = NodeProperties::GetValueInput(node, 2);
Type length_type = NodeProperties::GetType(length); Type length_type = NodeProperties::GetType(length);
...@@ -756,16 +751,18 @@ Reduction JSCreateLowering::ReduceJSCreateArray(Node* node) { ...@@ -756,16 +751,18 @@ Reduction JSCreateLowering::ReduceJSCreateArray(Node* node) {
: PACKED_ELEMENTS); : PACKED_ELEMENTS);
initial_map = initial_map.AsElementsKind(elements_kind); initial_map = initial_map.AsElementsKind(elements_kind);
return ReduceNewArray(node, std::vector<Node*>{length}, initial_map, return ReduceNewArray(node, std::vector<Node*>{length}, initial_map,
pretenure); pretenure, slack_tracking_prediction);
} }
if (length_type.Is(Type::SignedSmall()) && length_type.Min() >= 0 && if (length_type.Is(Type::SignedSmall()) && length_type.Min() >= 0 &&
length_type.Max() <= kElementLoopUnrollLimit && length_type.Max() <= kElementLoopUnrollLimit &&
length_type.Min() == length_type.Max()) { length_type.Min() == length_type.Max()) {
int capacity = static_cast<int>(length_type.Max()); int capacity = static_cast<int>(length_type.Max());
return ReduceNewArray(node, length, capacity, initial_map, pretenure); return ReduceNewArray(node, length, capacity, initial_map, pretenure,
slack_tracking_prediction);
} }
if (length_type.Maybe(Type::UnsignedSmall()) && can_inline_call) { if (length_type.Maybe(Type::UnsignedSmall()) && can_inline_call) {
return ReduceNewArray(node, length, initial_map, pretenure); return ReduceNewArray(node, length, initial_map, pretenure,
slack_tracking_prediction);
} }
} else if (arity <= JSArray::kInitialMaxFastElementArray) { } else if (arity <= JSArray::kInitialMaxFastElementArray) {
// Gather the values to store into the newly created array. // Gather the values to store into the newly created array.
...@@ -812,7 +809,8 @@ Reduction JSCreateLowering::ReduceJSCreateArray(Node* node) { ...@@ -812,7 +809,8 @@ Reduction JSCreateLowering::ReduceJSCreateArray(Node* node) {
} }
initial_map = initial_map.AsElementsKind(elements_kind); initial_map = initial_map.AsElementsKind(elements_kind);
return ReduceNewArray(node, values, initial_map, pretenure); return ReduceNewArray(node, values, initial_map, pretenure,
slack_tracking_prediction);
} }
} }
} }
...@@ -1158,7 +1156,11 @@ Reduction JSCreateLowering::ReduceJSCreateEmptyLiteralArray(Node* node) { ...@@ -1158,7 +1156,11 @@ Reduction JSCreateLowering::ReduceJSCreateEmptyLiteralArray(Node* node) {
PretenureFlag const pretenure = dependencies()->DependOnPretenureMode(site); PretenureFlag const pretenure = dependencies()->DependOnPretenureMode(site);
dependencies()->DependOnElementsKind(site); dependencies()->DependOnElementsKind(site);
Node* length = jsgraph()->ZeroConstant(); Node* length = jsgraph()->ZeroConstant();
return ReduceNewArray(node, length, 0, initial_map, pretenure); DCHECK(!initial_map.IsInobjectSlackTrackingInProgress());
SlackTrackingPrediction slack_tracking_prediction(
initial_map, initial_map.instance_size());
return ReduceNewArray(node, length, 0, initial_map, pretenure,
slack_tracking_prediction);
} }
return NoChange(); return NoChange();
} }
......
...@@ -26,7 +26,7 @@ class JSGraph; ...@@ -26,7 +26,7 @@ class JSGraph;
class JSOperatorBuilder; class JSOperatorBuilder;
class MachineOperatorBuilder; class MachineOperatorBuilder;
class SimplifiedOperatorBuilder; class SimplifiedOperatorBuilder;
class SlackTrackingPrediction;
// Lowers JSCreate-level operators to fast (inline) allocations. // Lowers JSCreate-level operators to fast (inline) allocations.
class V8_EXPORT_PRIVATE JSCreateLowering final class V8_EXPORT_PRIVATE JSCreateLowering final
...@@ -68,12 +68,17 @@ class V8_EXPORT_PRIVATE JSCreateLowering final ...@@ -68,12 +68,17 @@ class V8_EXPORT_PRIVATE JSCreateLowering final
Reduction ReduceJSCreateCatchContext(Node* node); Reduction ReduceJSCreateCatchContext(Node* node);
Reduction ReduceJSCreateBlockContext(Node* node); Reduction ReduceJSCreateBlockContext(Node* node);
Reduction ReduceJSCreateGeneratorObject(Node* node); Reduction ReduceJSCreateGeneratorObject(Node* node);
Reduction ReduceNewArray(Node* node, Node* length, MapRef initial_map, Reduction ReduceNewArray(
PretenureFlag pretenure); Node* node, Node* length, MapRef initial_map, PretenureFlag pretenure,
Reduction ReduceNewArray(Node* node, Node* length, int capacity, const SlackTrackingPrediction& slack_tracking_prediction);
MapRef initial_map, PretenureFlag pretenure); Reduction ReduceNewArray(
Reduction ReduceNewArray(Node* node, std::vector<Node*> values, Node* node, Node* length, int capacity, MapRef initial_map,
MapRef initial_map, PretenureFlag pretenure); PretenureFlag pretenure,
const SlackTrackingPrediction& slack_tracking_prediction);
Reduction ReduceNewArray(
Node* node, std::vector<Node*> values, MapRef initial_map,
PretenureFlag pretenure,
const SlackTrackingPrediction& slack_tracking_prediction);
Reduction ReduceJSCreateObject(Node* node); Reduction ReduceJSCreateObject(Node* node);
Node* AllocateArguments(Node* effect, Node* control, Node* frame_state); Node* AllocateArguments(Node* effect, Node* control, Node* frame_state);
......
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
#include "src/compiler/js-heap-broker.h" #include "src/compiler/js-heap-broker.h"
#include "src/compiler/compilation-dependencies.h"
#include "src/objects-inl.h" #include "src/objects-inl.h"
#include "src/objects/js-array-inl.h" #include "src/objects/js-array-inl.h"
#include "src/objects/js-regexp-inl.h" #include "src/objects/js-regexp-inl.h"
...@@ -204,6 +203,8 @@ StringRef ObjectRef::TypeOf() const { ...@@ -204,6 +203,8 @@ StringRef ObjectRef::TypeOf() const {
Object::TypeOf(broker()->isolate(), object<Object>())); Object::TypeOf(broker()->isolate(), object<Object>()));
} }
Isolate* ObjectRef::isolate() const { return broker()->isolate(); }
base::Optional<ContextRef> ContextRef::previous() const { base::Optional<ContextRef> ContextRef::previous() const {
AllowHandleAllocation handle_allocation; AllowHandleAllocation handle_allocation;
AllowHandleDereference handle_dereference; AllowHandleDereference handle_dereference;
...@@ -406,14 +407,12 @@ MapRef MapRef::AsElementsKind(ElementsKind kind) const { ...@@ -406,14 +407,12 @@ MapRef MapRef::AsElementsKind(ElementsKind kind) const {
Map::AsElementsKind(broker()->isolate(), object<Map>(), kind)); Map::AsElementsKind(broker()->isolate(), object<Map>(), kind));
} }
SlackTrackingResult JSFunctionRef::FinishSlackTracking() const { int JSFunctionRef::InitialMapInstanceSizeWithMinSlack() const {
AllowHandleDereference allow_handle_dereference; AllowHandleDereference allow_handle_dereference;
AllowHandleAllocation handle_allocation; AllowHandleAllocation handle_allocation;
object<JSFunction>()->CompleteInobjectSlackTrackingIfActive();
int instance_size = object<JSFunction>()->initial_map()->instance_size(); return object<JSFunction>()->ComputeInstanceSizeWithMinSlack(
int inobject_property_count = broker()->isolate());
object<JSFunction>()->initial_map()->GetInObjectProperties();
return SlackTrackingResult(instance_size, inobject_property_count);
} }
base::Optional<ScriptContextTableRef::LookupResult> base::Optional<ScriptContextTableRef::LookupResult>
...@@ -753,6 +752,7 @@ HANDLE_ACCESSOR_C(Map, bool, is_stable) ...@@ -753,6 +752,7 @@ HANDLE_ACCESSOR_C(Map, bool, is_stable)
HANDLE_ACCESSOR_C(Map, ElementsKind, elements_kind) HANDLE_ACCESSOR_C(Map, ElementsKind, elements_kind)
HANDLE_ACCESSOR_C(Map, InstanceType, instance_type) HANDLE_ACCESSOR_C(Map, InstanceType, instance_type)
HANDLE_ACCESSOR_C(Map, int, GetInObjectProperties) HANDLE_ACCESSOR_C(Map, int, GetInObjectProperties)
HANDLE_ACCESSOR_C(Map, int, GetInObjectPropertiesStartInWords)
HANDLE_ACCESSOR_C(Map, int, instance_size) HANDLE_ACCESSOR_C(Map, int, instance_size)
HANDLE_ACCESSOR_C(Map, int, NumberOfOwnDescriptors) HANDLE_ACCESSOR_C(Map, int, NumberOfOwnDescriptors)
HANDLE_ACCESSOR(Map, Object, constructor_or_backpointer) HANDLE_ACCESSOR(Map, Object, constructor_or_backpointer)
......
...@@ -120,6 +120,8 @@ class ObjectRef { ...@@ -120,6 +120,8 @@ class ObjectRef {
bool BooleanValue(); bool BooleanValue();
double OddballToNumber() const; double OddballToNumber() const;
Isolate* isolate() const;
protected: protected:
JSHeapBroker* broker() const; JSHeapBroker* broker() const;
ObjectData* data() const; ObjectData* data() const;
...@@ -160,14 +162,6 @@ class JSObjectRef : public HeapObjectRef { ...@@ -160,14 +162,6 @@ class JSObjectRef : public HeapObjectRef {
ElementsKind GetElementsKind() const; ElementsKind GetElementsKind() const;
}; };
struct SlackTrackingResult {
SlackTrackingResult(int instance_sizex, int inobject_property_countx)
: instance_size(instance_sizex),
inobject_property_count(inobject_property_countx) {}
int instance_size;
int inobject_property_count;
};
class JSFunctionRef : public JSObjectRef { class JSFunctionRef : public JSObjectRef {
public: public:
using JSObjectRef::JSObjectRef; using JSObjectRef::JSObjectRef;
...@@ -179,7 +173,7 @@ class JSFunctionRef : public JSObjectRef { ...@@ -179,7 +173,7 @@ class JSFunctionRef : public JSObjectRef {
MapRef initial_map() const; MapRef initial_map() const;
JSGlobalProxyRef global_proxy() const; JSGlobalProxyRef global_proxy() const;
SlackTrackingResult FinishSlackTracking() const; int InitialMapInstanceSizeWithMinSlack() const;
SharedFunctionInfoRef shared() const; SharedFunctionInfoRef shared() const;
void EnsureHasInitialMap() const; void EnsureHasInitialMap() const;
}; };
...@@ -288,6 +282,7 @@ class MapRef : public HeapObjectRef { ...@@ -288,6 +282,7 @@ class MapRef : public HeapObjectRef {
int instance_size() const; int instance_size() const;
InstanceType instance_type() const; InstanceType instance_type() const;
int GetInObjectProperties() const; int GetInObjectProperties() const;
int GetInObjectPropertiesStartInWords() const;
int NumberOfOwnDescriptors() const; int NumberOfOwnDescriptors() const;
PropertyDetails GetPropertyDetails(int i) const; PropertyDetails GetPropertyDetails(int i) const;
NameRef GetPropertyKey(int i) const; NameRef GetPropertyKey(int i) const;
......
...@@ -12559,6 +12559,9 @@ static void GetMinInobjectSlack(Map* map, void* data) { ...@@ -12559,6 +12559,9 @@ static void GetMinInobjectSlack(Map* map, void* data) {
} }
} }
int Map::InstanceSizeFromSlack(int slack) const {
return instance_size() - slack * kPointerSize;
}
static void ShrinkInstanceSize(Map* map, void* data) { static void ShrinkInstanceSize(Map* map, void* data) {
int slack = *reinterpret_cast<int*>(data); int slack = *reinterpret_cast<int*>(data);
...@@ -12567,7 +12570,7 @@ static void ShrinkInstanceSize(Map* map, void* data) { ...@@ -12567,7 +12570,7 @@ static void ShrinkInstanceSize(Map* map, void* data) {
int old_visitor_id = Map::GetVisitorId(map); int old_visitor_id = Map::GetVisitorId(map);
int new_unused = map->UnusedPropertyFields() - slack; int new_unused = map->UnusedPropertyFields() - slack;
#endif #endif
map->set_instance_size(map->instance_size() - slack * kPointerSize); map->set_instance_size(map->InstanceSizeFromSlack(slack));
map->set_construction_counter(Map::kNoSlackTracking); map->set_construction_counter(Map::kNoSlackTracking);
DCHECK_EQ(old_visitor_id, Map::GetVisitorId(map)); DCHECK_EQ(old_visitor_id, Map::GetVisitorId(map));
DCHECK_EQ(new_unused, map->UnusedPropertyFields()); DCHECK_EQ(new_unused, map->UnusedPropertyFields());
...@@ -12577,7 +12580,7 @@ static void StopSlackTracking(Map* map, void* data) { ...@@ -12577,7 +12580,7 @@ static void StopSlackTracking(Map* map, void* data) {
map->set_construction_counter(Map::kNoSlackTracking); map->set_construction_counter(Map::kNoSlackTracking);
} }
void Map::CompleteInobjectSlackTracking(Isolate* isolate) { int Map::ComputeMinObjectSlack(Isolate* isolate) {
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
// Has to be an initial map. // Has to be an initial map.
DCHECK(GetBackPointer()->IsUndefined(isolate)); DCHECK(GetBackPointer()->IsUndefined(isolate));
...@@ -12585,6 +12588,16 @@ void Map::CompleteInobjectSlackTracking(Isolate* isolate) { ...@@ -12585,6 +12588,16 @@ void Map::CompleteInobjectSlackTracking(Isolate* isolate) {
int slack = UnusedPropertyFields(); int slack = UnusedPropertyFields();
TransitionsAccessor transitions(isolate, this, &no_gc); TransitionsAccessor transitions(isolate, this, &no_gc);
transitions.TraverseTransitionTree(&GetMinInobjectSlack, &slack); transitions.TraverseTransitionTree(&GetMinInobjectSlack, &slack);
return slack;
}
void Map::CompleteInobjectSlackTracking(Isolate* isolate) {
DisallowHeapAllocation no_gc;
// Has to be an initial map.
DCHECK(GetBackPointer()->IsUndefined(isolate));
int slack = ComputeMinObjectSlack(isolate);
TransitionsAccessor transitions(isolate, this, &no_gc);
if (slack != 0) { if (slack != 0) {
// Resize the initial map and all maps in its transition tree. // Resize the initial map and all maps in its transition tree.
transitions.TraverseTransitionTree(&ShrinkInstanceSize, &slack); transitions.TraverseTransitionTree(&ShrinkInstanceSize, &slack);
...@@ -13353,6 +13366,14 @@ MaybeHandle<Map> JSFunction::GetDerivedMap(Isolate* isolate, ...@@ -13353,6 +13366,14 @@ MaybeHandle<Map> JSFunction::GetDerivedMap(Isolate* isolate,
return map; return map;
} }
int JSFunction::ComputeInstanceSizeWithMinSlack(Isolate* isolate) {
if (has_prototype_slot() && has_initial_map() &&
initial_map()->IsInobjectSlackTrackingInProgress()) {
int slack = initial_map()->ComputeMinObjectSlack(isolate);
return initial_map()->InstanceSizeFromSlack(slack);
}
return initial_map()->instance_size();
}
void JSFunction::PrintName(FILE* out) { void JSFunction::PrintName(FILE* out) {
std::unique_ptr<char[]> name = shared()->DebugName()->ToCString(); std::unique_ptr<char[]> name = shared()->DebugName()->ToCString();
......
...@@ -3250,6 +3250,11 @@ class JSFunction: public JSObject { ...@@ -3250,6 +3250,11 @@ class JSFunction: public JSObject {
// Clears the optimization marker in the function's feedback vector. // Clears the optimization marker in the function's feedback vector.
inline void ClearOptimizationMarker(); inline void ClearOptimizationMarker();
// If slack tracking is active, it computes instance size of the initial map
// with minimum permissible object slack. If it is not active, it simply
// returns the initial map's instance size.
int ComputeInstanceSizeWithMinSlack(Isolate* isolate);
// Completes inobject slack tracking on initial map if it is active. // Completes inobject slack tracking on initial map if it is active.
inline void CompleteInobjectSlackTrackingIfActive(); inline void CompleteInobjectSlackTrackingIfActive();
......
...@@ -330,6 +330,11 @@ class Map : public HeapObject { ...@@ -330,6 +330,11 @@ class Map : public HeapObject {
// Does the tracking step. // Does the tracking step.
inline void InobjectSlackTrackingStep(Isolate* isolate); inline void InobjectSlackTrackingStep(Isolate* isolate);
// Computes inobject slack for the transition tree starting at this initial
// map.
int ComputeMinObjectSlack(Isolate* isolate);
inline int InstanceSizeFromSlack(int slack) const;
// Completes inobject slack tracking for the transition tree starting at this // Completes inobject slack tracking for the transition tree starting at this
// initial map. // initial map.
void CompleteInobjectSlackTracking(Isolate* isolate); void CompleteInobjectSlackTracking(Isolate* isolate);
......
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