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)
class CompilationDependencies::Dependency : public ZoneObject {
public:
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 {
......@@ -36,9 +36,10 @@ class InitialMapDependency final : public CompilationDependencies::Dependency {
function->initial_map() == *initial_map_.object<Map>();
}
void Install(Isolate* isolate, MaybeObjectHandle code) override {
void Install(MaybeObjectHandle code) override {
SLOW_DCHECK(IsValid());
DependentCode::InstallDependency(isolate, code, initial_map_.object<Map>(),
DependentCode::InstallDependency(function_.isolate(), code,
initial_map_.object<Map>(),
DependentCode::kInitialMapChangedGroup);
}
......@@ -55,9 +56,9 @@ class StableMapDependency final : public CompilationDependencies::Dependency {
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());
DependentCode::InstallDependency(isolate, code, map_.object<Map>(),
DependentCode::InstallDependency(map_.isolate(), code, map_.object<Map>(),
DependentCode::kPrototypeCheckGroup);
}
......@@ -73,9 +74,9 @@ class TransitionDependency final : public CompilationDependencies::Dependency {
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());
DependentCode::InstallDependency(isolate, code, map_.object<Map>(),
DependentCode::InstallDependency(map_.isolate(), code, map_.object<Map>(),
DependentCode::kTransitionGroup);
}
......@@ -97,10 +98,10 @@ class PretenureModeDependency final
return mode_ == site_.object<AllocationSite>()->GetPretenureMode();
}
void Install(Isolate* isolate, MaybeObjectHandle code) override {
void Install(MaybeObjectHandle code) override {
SLOW_DCHECK(IsValid());
DependentCode::InstallDependency(
isolate, code, site_.object<AllocationSite>(),
site_.isolate(), code, site_.object<AllocationSite>(),
DependentCode::kAllocationSiteTenuringChangedGroup);
}
......@@ -127,9 +128,10 @@ class FieldTypeDependency final : public CompilationDependencies::Dependency {
return *type == owner->instance_descriptors()->GetFieldType(descriptor_);
}
void Install(Isolate* isolate, MaybeObjectHandle code) override {
void Install(MaybeObjectHandle code) override {
SLOW_DCHECK(IsValid());
DependentCode::InstallDependency(isolate, code, owner_.object<Map>(),
DependentCode::InstallDependency(owner_.isolate(), code,
owner_.object<Map>(),
DependentCode::kFieldOwnerGroup);
}
......@@ -157,9 +159,9 @@ class GlobalPropertyDependency final
read_only_ == cell->property_details().IsReadOnly();
}
void Install(Isolate* isolate, MaybeObjectHandle code) override {
void Install(MaybeObjectHandle code) override {
SLOW_DCHECK(IsValid());
DependentCode::InstallDependency(isolate, code,
DependentCode::InstallDependency(cell_.isolate(), code,
cell_.object<PropertyCell>(),
DependentCode::kPropertyCellChangedGroup);
}
......@@ -181,9 +183,9 @@ class ProtectorDependency final : public CompilationDependencies::Dependency {
return cell->value() == Smi::FromInt(Isolate::kProtectorValid);
}
void Install(Isolate* isolate, MaybeObjectHandle code) override {
void Install(MaybeObjectHandle code) override {
SLOW_DCHECK(IsValid());
DependentCode::InstallDependency(isolate, code,
DependentCode::InstallDependency(cell_.isolate(), code,
cell_.object<PropertyCell>(),
DependentCode::kPropertyCellChangedGroup);
}
......@@ -213,10 +215,10 @@ class ElementsKindDependency final
return kind_ == kind;
}
void Install(Isolate* isolate, MaybeObjectHandle code) override {
void Install(MaybeObjectHandle code) override {
SLOW_DCHECK(IsValid());
DependentCode::InstallDependency(
isolate, code, site_.object<AllocationSite>(),
site_.isolate(), code, site_.object<AllocationSite>(),
DependentCode::kAllocationSiteTransitionChangedGroup);
}
......@@ -225,6 +227,33 @@ class ElementsKindDependency final
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(
const JSFunctionRef& function) {
MapRef map = function.initial_map();
......@@ -295,8 +324,6 @@ bool CompilationDependencies::AreValid() const {
}
bool CompilationDependencies::Commit(Handle<Code> code) {
Isolate* isolate = code->GetIsolate();
// Check validity of all dependencies first, such that we can avoid installing
// anything when there's already an invalid dependency.
if (!AreValid()) {
......@@ -311,7 +338,7 @@ bool CompilationDependencies::Commit(Handle<Code> code) {
dependencies_.clear();
return false;
}
dep->Install(isolate, MaybeObjectHandle::Weak(code));
dep->Install(MaybeObjectHandle::Weak(code));
}
dependencies_.clear();
return true;
......@@ -363,6 +390,28 @@ void CompilationDependencies::DependOnElementsKinds(
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 internal
} // namespace v8
......@@ -13,6 +13,18 @@ namespace v8 {
namespace internal {
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.
class V8_EXPORT_PRIVATE CompilationDependencies : public ZoneObject {
public:
......@@ -21,7 +33,7 @@ class V8_EXPORT_PRIVATE CompilationDependencies : public ZoneObject {
V8_WARN_UNUSED_RESULT bool Commit(Handle<Code> code);
// 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);
// Record the assumption that {map} stays stable.
......@@ -59,6 +71,14 @@ class V8_EXPORT_PRIVATE CompilationDependencies : public ZoneObject {
// Like DependOnElementsKind but also applies to all nested allocation sites.
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.
bool AreValid() const;
......
This diff is collapsed.
......@@ -26,7 +26,7 @@ class JSGraph;
class JSOperatorBuilder;
class MachineOperatorBuilder;
class SimplifiedOperatorBuilder;
class SlackTrackingPrediction;
// Lowers JSCreate-level operators to fast (inline) allocations.
class V8_EXPORT_PRIVATE JSCreateLowering final
......@@ -68,12 +68,17 @@ class V8_EXPORT_PRIVATE JSCreateLowering final
Reduction ReduceJSCreateCatchContext(Node* node);
Reduction ReduceJSCreateBlockContext(Node* node);
Reduction ReduceJSCreateGeneratorObject(Node* node);
Reduction ReduceNewArray(Node* node, Node* length, MapRef initial_map,
PretenureFlag pretenure);
Reduction ReduceNewArray(Node* node, Node* length, int capacity,
MapRef initial_map, PretenureFlag pretenure);
Reduction ReduceNewArray(Node* node, std::vector<Node*> values,
MapRef initial_map, PretenureFlag pretenure);
Reduction ReduceNewArray(
Node* node, Node* length, MapRef initial_map, PretenureFlag pretenure,
const SlackTrackingPrediction& slack_tracking_prediction);
Reduction ReduceNewArray(
Node* node, Node* length, int capacity, MapRef initial_map,
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);
Node* AllocateArguments(Node* effect, Node* control, Node* frame_state);
......
......@@ -4,7 +4,6 @@
#include "src/compiler/js-heap-broker.h"
#include "src/compiler/compilation-dependencies.h"
#include "src/objects-inl.h"
#include "src/objects/js-array-inl.h"
#include "src/objects/js-regexp-inl.h"
......@@ -204,6 +203,8 @@ StringRef ObjectRef::TypeOf() const {
Object::TypeOf(broker()->isolate(), object<Object>()));
}
Isolate* ObjectRef::isolate() const { return broker()->isolate(); }
base::Optional<ContextRef> ContextRef::previous() const {
AllowHandleAllocation handle_allocation;
AllowHandleDereference handle_dereference;
......@@ -406,14 +407,12 @@ MapRef MapRef::AsElementsKind(ElementsKind kind) const {
Map::AsElementsKind(broker()->isolate(), object<Map>(), kind));
}
SlackTrackingResult JSFunctionRef::FinishSlackTracking() const {
int JSFunctionRef::InitialMapInstanceSizeWithMinSlack() const {
AllowHandleDereference allow_handle_dereference;
AllowHandleAllocation handle_allocation;
object<JSFunction>()->CompleteInobjectSlackTrackingIfActive();
int instance_size = object<JSFunction>()->initial_map()->instance_size();
int inobject_property_count =
object<JSFunction>()->initial_map()->GetInObjectProperties();
return SlackTrackingResult(instance_size, inobject_property_count);
return object<JSFunction>()->ComputeInstanceSizeWithMinSlack(
broker()->isolate());
}
base::Optional<ScriptContextTableRef::LookupResult>
......@@ -753,6 +752,7 @@ HANDLE_ACCESSOR_C(Map, bool, is_stable)
HANDLE_ACCESSOR_C(Map, ElementsKind, elements_kind)
HANDLE_ACCESSOR_C(Map, InstanceType, instance_type)
HANDLE_ACCESSOR_C(Map, int, GetInObjectProperties)
HANDLE_ACCESSOR_C(Map, int, GetInObjectPropertiesStartInWords)
HANDLE_ACCESSOR_C(Map, int, instance_size)
HANDLE_ACCESSOR_C(Map, int, NumberOfOwnDescriptors)
HANDLE_ACCESSOR(Map, Object, constructor_or_backpointer)
......
......@@ -120,6 +120,8 @@ class ObjectRef {
bool BooleanValue();
double OddballToNumber() const;
Isolate* isolate() const;
protected:
JSHeapBroker* broker() const;
ObjectData* data() const;
......@@ -160,14 +162,6 @@ class JSObjectRef : public HeapObjectRef {
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 {
public:
using JSObjectRef::JSObjectRef;
......@@ -179,7 +173,7 @@ class JSFunctionRef : public JSObjectRef {
MapRef initial_map() const;
JSGlobalProxyRef global_proxy() const;
SlackTrackingResult FinishSlackTracking() const;
int InitialMapInstanceSizeWithMinSlack() const;
SharedFunctionInfoRef shared() const;
void EnsureHasInitialMap() const;
};
......@@ -288,6 +282,7 @@ class MapRef : public HeapObjectRef {
int instance_size() const;
InstanceType instance_type() const;
int GetInObjectProperties() const;
int GetInObjectPropertiesStartInWords() const;
int NumberOfOwnDescriptors() const;
PropertyDetails GetPropertyDetails(int i) const;
NameRef GetPropertyKey(int i) const;
......
......@@ -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) {
int slack = *reinterpret_cast<int*>(data);
......@@ -12567,7 +12570,7 @@ static void ShrinkInstanceSize(Map* map, void* data) {
int old_visitor_id = Map::GetVisitorId(map);
int new_unused = map->UnusedPropertyFields() - slack;
#endif
map->set_instance_size(map->instance_size() - slack * kPointerSize);
map->set_instance_size(map->InstanceSizeFromSlack(slack));
map->set_construction_counter(Map::kNoSlackTracking);
DCHECK_EQ(old_visitor_id, Map::GetVisitorId(map));
DCHECK_EQ(new_unused, map->UnusedPropertyFields());
......@@ -12577,7 +12580,7 @@ static void StopSlackTracking(Map* map, void* data) {
map->set_construction_counter(Map::kNoSlackTracking);
}
void Map::CompleteInobjectSlackTracking(Isolate* isolate) {
int Map::ComputeMinObjectSlack(Isolate* isolate) {
DisallowHeapAllocation no_gc;
// Has to be an initial map.
DCHECK(GetBackPointer()->IsUndefined(isolate));
......@@ -12585,6 +12588,16 @@ void Map::CompleteInobjectSlackTracking(Isolate* isolate) {
int slack = UnusedPropertyFields();
TransitionsAccessor transitions(isolate, this, &no_gc);
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) {
// Resize the initial map and all maps in its transition tree.
transitions.TraverseTransitionTree(&ShrinkInstanceSize, &slack);
......@@ -13353,6 +13366,14 @@ MaybeHandle<Map> JSFunction::GetDerivedMap(Isolate* isolate,
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) {
std::unique_ptr<char[]> name = shared()->DebugName()->ToCString();
......
......@@ -3250,6 +3250,11 @@ class JSFunction: public JSObject {
// Clears the optimization marker in the function's feedback vector.
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.
inline void CompleteInobjectSlackTrackingIfActive();
......
......@@ -330,6 +330,11 @@ class Map : public HeapObject {
// Does the tracking step.
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
// initial map.
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