Reland: track global accesses to constant types

R=verwaest@chromium.org
BUG=468620
LOG=N

Review URL: https://codereview.chromium.org/1102543002

Cr-Commit-Position: refs/heads/master@{#28081}
This commit is contained in:
dcarney 2015-04-27 07:01:15 -07:00 committed by Commit bot
parent 0be81a43ea
commit 1d2be2af40
7 changed files with 203 additions and 68 deletions

View File

@ -1340,10 +1340,10 @@ HValue* CodeStubGraphBuilder<StoreGlobalStub>::BuildCodeInitializedStub() {
Add<HLoadNamedField>(proxy, nullptr, HObjectAccess::ForMap());
HValue* global =
Add<HLoadNamedField>(proxy_map, nullptr, HObjectAccess::ForPrototype());
Handle<Map> placeholder_map = isolate()->factory()->meta_map();
HValue* cell = Add<HConstant>(Map::WeakCellForMap(placeholder_map));
HValue* expected_map =
Add<HLoadNamedField>(cell, nullptr, HObjectAccess::ForWeakCellValue());
HValue* map_cell = Add<HConstant>(isolate()->factory()->NewWeakCell(
StoreGlobalStub::global_map_placeholder(isolate())));
HValue* expected_map = Add<HLoadNamedField>(
map_cell, nullptr, HObjectAccess::ForWeakCellValue());
HValue* map =
Add<HLoadNamedField>(global, nullptr, HObjectAccess::ForMap());
IfBuilder map_check(this);
@ -1358,9 +1358,15 @@ HValue* CodeStubGraphBuilder<StoreGlobalStub>::BuildCodeInitializedStub() {
HObjectAccess::ForWeakCellValue());
Add<HCheckHeapObject>(cell);
HObjectAccess access = HObjectAccess::ForPropertyCellValue();
// Load the payload of the global parameter cell. A hole indicates that the
// cell has been invalidated and that the store must be handled by the
// runtime.
HValue* cell_contents = Add<HLoadNamedField>(cell, nullptr, access);
if (stub->is_constant()) {
auto cell_type = stub->cell_type();
if (cell_type == PropertyCellType::kConstant ||
cell_type == PropertyCellType::kUndefined) {
// This is always valid for all states a cell can be in.
IfBuilder builder(this);
builder.If<HCompareObjectEqAndBranch>(cell_contents, value);
builder.Then();
@ -1368,15 +1374,40 @@ HValue* CodeStubGraphBuilder<StoreGlobalStub>::BuildCodeInitializedStub() {
Deoptimizer::kUnexpectedCellContentsInConstantGlobalStore);
builder.End();
} else {
// Load the payload of the global parameter cell. A hole indicates that the
// property has been deleted and that the store must be handled by the
// runtime.
IfBuilder builder(this);
HValue* hole_value = graph()->GetConstantHole();
builder.If<HCompareObjectEqAndBranch>(cell_contents, hole_value);
builder.Then();
builder.Deopt(Deoptimizer::kUnexpectedCellContentsInGlobalStore);
builder.Else();
// When dealing with constant types, the type may be allowed to change, as
// long as optimized code remains valid.
if (cell_type == PropertyCellType::kConstantType) {
switch (stub->constant_type()) {
case PropertyCellConstantType::kSmi:
access = access.WithRepresentation(Representation::Smi());
break;
case PropertyCellConstantType::kStableMap: {
// It is sufficient here to check that the value and cell contents
// have identical maps, no matter if they are stable or not or if they
// are the maps that were originally in the cell or not. If optimized
// code will deopt when a cell has a unstable map and if it has a
// dependency on a stable map, it will deopt if the map destabilizes.
Add<HCheckHeapObject>(value);
Add<HCheckHeapObject>(cell_contents);
HValue* expected_map = Add<HLoadNamedField>(cell_contents, nullptr,
HObjectAccess::ForMap());
HValue* map =
Add<HLoadNamedField>(value, nullptr, HObjectAccess::ForMap());
IfBuilder map_check(this);
map_check.IfNot<HCompareObjectEqAndBranch>(expected_map, map);
map_check.ThenDeopt(Deoptimizer::kUnknownMap);
map_check.End();
access = access.WithRepresentation(Representation::HeapObject());
break;
}
}
}
Add<HStoreNamedField>(cell, access, value);
builder.End();
}

View File

@ -1137,9 +1137,14 @@ class StoreTransitionStub : public HandlerStub {
class StoreGlobalStub : public HandlerStub {
public:
StoreGlobalStub(Isolate* isolate, bool is_constant, bool check_global)
StoreGlobalStub(Isolate* isolate, PropertyCellType type,
Maybe<PropertyCellConstantType> constant_type,
bool check_global)
: HandlerStub(isolate) {
set_sub_minor_key(IsConstantBits::encode(is_constant) |
PropertyCellConstantType encoded_constant_type =
constant_type.FromMaybe(PropertyCellConstantType::kSmi);
set_sub_minor_key(CellTypeBits::encode(type) |
ConstantTypeBits::encode(encoded_constant_type) |
CheckGlobalBits::encode(check_global));
}
@ -1147,33 +1152,35 @@ class StoreGlobalStub : public HandlerStub {
return isolate->factory()->uninitialized_value();
}
static Handle<HeapObject> global_map_placeholder(Isolate* isolate) {
return isolate->factory()->termination_exception();
}
Handle<Code> GetCodeCopyFromTemplate(Handle<GlobalObject> global,
Handle<PropertyCell> cell) {
Code::FindAndReplacePattern pattern;
if (check_global()) {
Code::FindAndReplacePattern pattern;
pattern.Add(isolate()->factory()->meta_map(),
pattern.Add(handle(global_map_placeholder(isolate())->map()),
Map::WeakCellForMap(Handle<Map>(global->map())));
pattern.Add(Handle<Map>(property_cell_placeholder(isolate())->map()),
isolate()->factory()->NewWeakCell(cell));
return CodeStub::GetCodeCopy(pattern);
} else {
Code::FindAndReplacePattern pattern;
pattern.Add(Handle<Map>(property_cell_placeholder(isolate())->map()),
isolate()->factory()->NewWeakCell(cell));
return CodeStub::GetCodeCopy(pattern);
}
pattern.Add(handle(property_cell_placeholder(isolate())->map()),
isolate()->factory()->NewWeakCell(cell));
return CodeStub::GetCodeCopy(pattern);
}
Code::Kind kind() const override { return Code::STORE_IC; }
bool is_constant() const { return IsConstantBits::decode(sub_minor_key()); }
PropertyCellType cell_type() const {
return CellTypeBits::decode(sub_minor_key());
}
PropertyCellConstantType constant_type() const {
DCHECK(PropertyCellType::kConstantType == cell_type());
return ConstantTypeBits::decode(sub_minor_key());
}
bool check_global() const { return CheckGlobalBits::decode(sub_minor_key()); }
void set_is_constant(bool value) {
set_sub_minor_key(IsConstantBits::update(sub_minor_key(), value));
}
Representation representation() {
return Representation::FromKind(
RepresentationBits::decode(sub_minor_key()));
@ -1184,9 +1191,10 @@ class StoreGlobalStub : public HandlerStub {
}
private:
class IsConstantBits: public BitField<bool, 0, 1> {};
class RepresentationBits: public BitField<Representation::Kind, 1, 8> {};
class CheckGlobalBits: public BitField<bool, 9, 1> {};
class CellTypeBits : public BitField<PropertyCellType, 0, 2> {};
class ConstantTypeBits : public BitField<PropertyCellConstantType, 2, 2> {};
class RepresentationBits : public BitField<Representation::Kind, 4, 8> {};
class CheckGlobalBits : public BitField<bool, 12, 1> {};
DEFINE_HANDLER_CODE_STUB(StoreGlobal, HandlerStub);
};

View File

@ -5464,7 +5464,9 @@ void HOptimizedGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
if (type == kUseCell) {
Handle<PropertyCell> cell = it.GetPropertyCell();
top_info()->dependencies()->AssumePropertyCell(cell);
if (it.property_details().cell_type() == PropertyCellType::kConstant) {
auto cell_type = it.property_details().cell_type();
if (cell_type == PropertyCellType::kConstant ||
cell_type == PropertyCellType::kUndefined) {
Handle<Object> constant_object(cell->value(), isolate());
if (constant_object->IsConsString()) {
constant_object =
@ -5473,9 +5475,37 @@ void HOptimizedGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
HConstant* constant = New<HConstant>(constant_object);
return ast_context()->ReturnInstruction(constant, expr->id());
} else {
auto access = HObjectAccess::ForPropertyCellValue();
UniqueSet<Map>* field_maps = nullptr;
if (cell_type == PropertyCellType::kConstantType) {
switch (cell->GetConstantType()) {
case PropertyCellConstantType::kSmi:
access = access.WithRepresentation(Representation::Smi());
break;
case PropertyCellConstantType::kStableMap: {
// Check that the map really is stable. The heap object could
// have mutated without the cell updating state. In that case,
// make no promises about the loaded value except that it's a
// heap object.
access =
access.WithRepresentation(Representation::HeapObject());
Handle<Map> map(HeapObject::cast(cell->value())->map());
if (map->is_stable()) {
field_maps = new (zone())
UniqueSet<Map>(Unique<Map>::CreateImmovable(map), zone());
}
break;
}
}
}
HConstant* cell_constant = Add<HConstant>(cell);
HLoadNamedField* instr = New<HLoadNamedField>(
cell_constant, nullptr, HObjectAccess::ForPropertyCellValue());
HLoadNamedField* instr;
if (field_maps == nullptr) {
instr = New<HLoadNamedField>(cell_constant, nullptr, access);
} else {
instr = New<HLoadNamedField>(cell_constant, nullptr, access,
field_maps, HType::HeapObject());
}
instr->ClearDependsOnFlag(kInobjectFields);
instr->SetDependsOnFlag(kGlobalVars);
return ast_context()->ReturnInstruction(instr, expr->id());
@ -6649,7 +6679,9 @@ void HOptimizedGraphBuilder::HandleGlobalVariableAssignment(
if (type == kUseCell) {
Handle<PropertyCell> cell = it.GetPropertyCell();
top_info()->dependencies()->AssumePropertyCell(cell);
if (it.property_details().cell_type() == PropertyCellType::kConstant) {
auto cell_type = it.property_details().cell_type();
if (cell_type == PropertyCellType::kConstant ||
cell_type == PropertyCellType::kUndefined) {
Handle<Object> constant(cell->value(), isolate());
if (value->IsConstant()) {
HConstant* c_value = HConstant::cast(value);
@ -6673,8 +6705,24 @@ void HOptimizedGraphBuilder::HandleGlobalVariableAssignment(
}
}
HConstant* cell_constant = Add<HConstant>(cell);
HInstruction* instr = Add<HStoreNamedField>(
cell_constant, HObjectAccess::ForPropertyCellValue(), value);
auto access = HObjectAccess::ForPropertyCellValue();
if (cell_type == PropertyCellType::kConstantType) {
switch (cell->GetConstantType()) {
case PropertyCellConstantType::kSmi:
access = access.WithRepresentation(Representation::Smi());
break;
case PropertyCellConstantType::kStableMap: {
// The map may no longer be stable, deopt if it's ever different from
// what is currently there, which will allow for restablization.
Handle<Map> map(HeapObject::cast(cell->value())->map());
Add<HCheckHeapObject>(value);
value = Add<HCheckMaps>(value, map);
access = access.WithRepresentation(Representation::HeapObject());
break;
}
}
}
HInstruction* instr = Add<HStoreNamedField>(cell_constant, access, value);
instr->ClearChangesFlag(kInobjectFields);
instr->SetChangesFlag(kGlobalVars);
if (instr->HasObservableSideEffects()) {

View File

@ -1720,7 +1720,11 @@ void StoreIC::UpdateCaches(LookupIterator* lookup, Handle<Object> value,
static Handle<Code> PropertyCellStoreHandler(
Isolate* isolate, Handle<JSObject> receiver, Handle<GlobalObject> holder,
Handle<Name> name, Handle<PropertyCell> cell, PropertyCellType type) {
StoreGlobalStub stub(isolate, type == PropertyCellType::kConstant,
auto constant_type = Nothing<PropertyCellConstantType>();
if (type == PropertyCellType::kConstantType) {
constant_type = Just(cell->GetConstantType());
}
StoreGlobalStub stub(isolate, type, constant_type,
receiver->IsJSGlobalProxy());
auto code = stub.GetCodeCopyFromTemplate(holder, cell);
// TODO(verwaest): Move caching of these NORMAL stubs outside as well.
@ -1821,11 +1825,12 @@ Handle<Code> StoreIC::CompileHandler(LookupIterator* lookup,
DCHECK(holder.is_identical_to(receiver) ||
receiver->map()->prototype() == *holder);
auto cell = lookup->GetPropertyCell();
auto union_type = PropertyCell::UpdatedType(
auto updated_type = PropertyCell::UpdatedType(
cell, value, lookup->property_details());
return PropertyCellStoreHandler(isolate(), receiver,
Handle<GlobalObject>::cast(holder),
lookup->name(), cell, union_type);
auto code = PropertyCellStoreHandler(
isolate(), receiver, Handle<GlobalObject>::cast(holder),
lookup->name(), cell, updated_type);
return code;
}
DCHECK(holder.is_identical_to(receiver));
return isolate()->builtins()->StoreIC_Normal();

View File

@ -1786,7 +1786,7 @@ void JSObject::AddSlowProperty(Handle<JSObject> object,
DCHECK(!object->HasFastProperties());
Isolate* isolate = object->GetIsolate();
Handle<NameDictionary> dict(object->property_dictionary());
PropertyDetails details(attributes, DATA, 0, PropertyCellType::kInvalid);
PropertyDetails details(attributes, DATA, 0, PropertyCellType::kNoCell);
if (object->IsGlobalObject()) {
int entry = dict->FindEntry(name);
// If there's a cell there, just invalidate and set the property.
@ -4618,7 +4618,7 @@ void JSObject::MigrateFastToSlow(Handle<JSObject> object,
case DATA_CONSTANT: {
Handle<Object> value(descs->GetConstant(i), isolate);
PropertyDetails d(details.attributes(), DATA, i + 1,
PropertyCellType::kInvalid);
PropertyCellType::kNoCell);
dictionary = NameDictionary::Add(dictionary, key, value, d);
break;
}
@ -4637,7 +4637,7 @@ void JSObject::MigrateFastToSlow(Handle<JSObject> object,
}
}
PropertyDetails d(details.attributes(), DATA, i + 1,
PropertyCellType::kInvalid);
PropertyCellType::kNoCell);
dictionary = NameDictionary::Add(dictionary, key, value, d);
break;
}
@ -4645,14 +4645,14 @@ void JSObject::MigrateFastToSlow(Handle<JSObject> object,
FieldIndex index = FieldIndex::ForDescriptor(*map, i);
Handle<Object> value(object->RawFastPropertyAt(index), isolate);
PropertyDetails d(details.attributes(), ACCESSOR_CONSTANT, i + 1,
PropertyCellType::kInvalid);
PropertyCellType::kNoCell);
dictionary = NameDictionary::Add(dictionary, key, value, d);
break;
}
case ACCESSOR_CONSTANT: {
Handle<Object> value(descs->GetCallbacksObject(i), isolate);
PropertyDetails d(details.attributes(), ACCESSOR_CONSTANT, i + 1,
PropertyCellType::kInvalid);
PropertyCellType::kNoCell);
dictionary = NameDictionary::Add(dictionary, key, value, d);
break;
}
@ -5382,7 +5382,7 @@ void JSObject::DeleteNormalizedProperty(Handle<JSObject> object,
cell->set_value(isolate->heap()->the_hole_value());
// TODO(dcarney): InvalidateForDelete
dictionary->DetailsAtPut(entry, dictionary->DetailsAt(entry).set_cell_type(
PropertyCellType::kDeleted));
PropertyCellType::kInvalidated));
return;
}
@ -6457,7 +6457,7 @@ static bool UpdateGetterSetterInDictionary(
if (details.attributes() != attributes) {
dictionary->DetailsAtPut(
entry, PropertyDetails(attributes, ACCESSOR_CONSTANT, index,
PropertyCellType::kInvalid));
PropertyCellType::kNoCell));
}
AccessorPair::cast(result)->SetComponents(getter, setter);
return true;
@ -6560,7 +6560,7 @@ void JSObject::SetElementCallback(Handle<JSObject> object,
PropertyAttributes attributes) {
Heap* heap = object->GetHeap();
PropertyDetails details = PropertyDetails(attributes, ACCESSOR_CONSTANT, 0,
PropertyCellType::kInvalid);
PropertyCellType::kNoCell);
// Normalize elements to make this operation simple.
bool had_dictionary_elements = object->HasDictionaryElements();
@ -12974,7 +12974,7 @@ MaybeHandle<Object> JSObject::SetDictionaryElement(
dictionary->UpdateMaxNumberKey(index);
if (set_mode == DEFINE_PROPERTY) {
details = PropertyDetails(attributes, DATA, details.dictionary_index(),
PropertyCellType::kInvalid);
PropertyCellType::kNoCell);
dictionary->DetailsAtPut(entry, details);
}
@ -13017,7 +13017,7 @@ MaybeHandle<Object> JSObject::SetDictionaryElement(
}
}
PropertyDetails details(attributes, DATA, 0, PropertyCellType::kInvalid);
PropertyDetails details(attributes, DATA, 0, PropertyCellType::kNoCell);
Handle<SeededNumberDictionary> new_dictionary =
SeededNumberDictionary::AddNumberEntry(dictionary, index, value,
details);
@ -15464,7 +15464,7 @@ Handle<PropertyCell> GlobalObject::EnsurePropertyCell(
DCHECK(dictionary->DetailsAt(entry).cell_type() ==
PropertyCellType::kUninitialized ||
dictionary->DetailsAt(entry).cell_type() ==
PropertyCellType::kDeleted);
PropertyCellType::kInvalidated);
DCHECK(dictionary->ValueAt(entry)->IsPropertyCell());
cell = handle(PropertyCell::cast(dictionary->ValueAt(entry)));
DCHECK(cell->value()->IsTheHole());
@ -17072,7 +17072,7 @@ Handle<PropertyCell> PropertyCell::InvalidateEntry(
bool is_the_hole = cell->value()->IsTheHole();
// Cell is officially mutable henceforth.
auto details = dictionary->DetailsAt(entry);
details = details.set_cell_type(is_the_hole ? PropertyCellType::kDeleted
details = details.set_cell_type(is_the_hole ? PropertyCellType::kInvalidated
: PropertyCellType::kMutable);
dictionary->DetailsAtPut(entry, details);
// Old cell is ready for invalidation.
@ -17087,25 +17087,55 @@ Handle<PropertyCell> PropertyCell::InvalidateEntry(
}
PropertyCellConstantType PropertyCell::GetConstantType() {
if (value()->IsSmi()) return PropertyCellConstantType::kSmi;
return PropertyCellConstantType::kStableMap;
}
static bool RemainsConstantType(Handle<PropertyCell> cell,
Handle<Object> value) {
// TODO(dcarney): double->smi and smi->double transition from kConstant
if (cell->value()->IsSmi() && value->IsSmi()) {
return true;
} else if (cell->value()->IsHeapObject() && value->IsHeapObject()) {
return HeapObject::cast(cell->value())->map() ==
HeapObject::cast(*value)->map() &&
HeapObject::cast(*value)->map()->is_stable();
}
return false;
}
PropertyCellType PropertyCell::UpdatedType(Handle<PropertyCell> cell,
Handle<Object> value,
PropertyDetails details) {
PropertyCellType type = details.cell_type();
DCHECK(!value->IsTheHole());
DCHECK_IMPLIES(cell->value()->IsTheHole(),
type == PropertyCellType::kUninitialized ||
type == PropertyCellType::kDeleted);
if (cell->value()->IsTheHole()) {
switch (type) {
// Only allow a cell to transition once into constant state.
case PropertyCellType::kUninitialized:
if (value->IsUndefined()) return PropertyCellType::kUndefined;
return PropertyCellType::kConstant;
case PropertyCellType::kInvalidated:
return PropertyCellType::kMutable;
default:
UNREACHABLE();
return PropertyCellType::kMutable;
}
}
switch (type) {
// Only allow a cell to transition once into constant state.
case PropertyCellType::kUninitialized:
if (value->IsUndefined()) return PropertyCellType::kUndefined;
return PropertyCellType::kConstant;
case PropertyCellType::kUndefined:
return PropertyCellType::kConstant;
case PropertyCellType::kConstant:
// No transition.
if (*value == cell->value()) return PropertyCellType::kConstant;
// Fall through.
case PropertyCellType::kConstantType:
if (RemainsConstantType(cell, value)) {
return PropertyCellType::kConstantType;
}
// Fall through.
case PropertyCellType::kMutable:
return PropertyCellType::kMutable;
}
@ -17158,8 +17188,7 @@ Handle<Object> PropertyCell::UpdateCell(Handle<NameDictionary> dictionary,
cell->set_value(*value);
// Deopt when transitioning from a constant type.
if (!invalidate && old_type == PropertyCellType::kConstant &&
new_type != PropertyCellType::kConstant) {
if (!invalidate && (old_type != new_type)) {
auto isolate = dictionary->GetIsolate();
cell->dependent_code()->DeoptimizeDependentCodeGroup(
isolate, DependentCode::kPropertyCellChangedGroup);

View File

@ -9837,6 +9837,8 @@ class PropertyCell : public HeapObject {
// property.
DECL_ACCESSORS(dependent_code, DependentCode)
PropertyCellConstantType GetConstantType();
// Computes the new type of the cell's contents for the given value, but
// without actually modifying the details.
static PropertyCellType UpdatedType(Handle<PropertyCell> cell,

View File

@ -186,12 +186,24 @@ static const int kInvalidEnumCacheSentinel =
enum class PropertyCellType {
kUninitialized, // Cell is deleted or not yet defined.
kUndefined, // The PREMONOMORPHIC of property cells.
kConstant, // Cell has been assigned only once.
kMutable, // Cell will no longer be tracked as constant.
kDeleted = kConstant, // like kUninitialized, but for cells already deleted.
kInvalid = kMutable, // For dictionaries not holding cells.
// Meaningful when a property cell does not contain the hole.
kUndefined, // The PREMONOMORPHIC of property cells.
kConstant, // Cell has been assigned only once.
kConstantType, // Cell has been assigned only one type.
kMutable, // Cell will no longer be tracked as constant.
// Meaningful when a property cell contains the hole.
kUninitialized = kUndefined, // Cell has never been initialized.
kInvalidated = kConstant, // Cell has been deleted or invalidated.
// For dictionaries not holding cells.
kNoCell = kMutable,
};
enum class PropertyCellConstantType {
kSmi,
kStableMap,
};
@ -229,7 +241,7 @@ class PropertyDetails BASE_EMBEDDED {
}
static PropertyDetails Empty() {
return PropertyDetails(NONE, DATA, 0, PropertyCellType::kInvalid);
return PropertyDetails(NONE, DATA, 0, PropertyCellType::kNoCell);
}
int pointer() const { return DescriptorPointer::decode(value_); }