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);
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();
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();
}
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));
}
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()); }
-
- bool check_global() const { return CheckGlobalBits::decode(sub_minor_key()); }
+ PropertyCellType cell_type() const {
+ return CellTypeBits::decode(sub_minor_key());
+ }
- void set_is_constant(bool value) {
- set_sub_minor_key(IsConstantBits::update(sub_minor_key(), value));
+ 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()); }
+
Representation representation() {
return Representation::FromKind(
RepresentationBits::decode(sub_minor_key()));
}
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);
};
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 =
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());
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);
}
}
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()) {
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.
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();
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.
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;
}
}
}
PropertyDetails d(details.attributes(), DATA, i + 1,
- PropertyCellType::kInvalid);
+ PropertyCellType::kNoCell);
dictionary = NameDictionary::Add(dictionary, key, value, d);
break;
}
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;
}
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;
}
if (details.attributes() != attributes) {
dictionary->DetailsAtPut(
entry, PropertyDetails(attributes, ACCESSOR_CONSTANT, index,
- PropertyCellType::kInvalid));
+ PropertyCellType::kNoCell));
}
AccessorPair::cast(result)->SetComponents(getter, setter);
return true;
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();
dictionary->UpdateMaxNumberKey(index);
if (set_mode == DEFINE_PROPERTY) {
details = PropertyDetails(attributes, DATA, details.dictionary_index(),
- PropertyCellType::kInvalid);
+ PropertyCellType::kNoCell);
dictionary->DetailsAtPut(entry, details);
}
}
}
- PropertyDetails details(attributes, DATA, 0, PropertyCellType::kInvalid);
+ PropertyDetails details(attributes, DATA, 0, PropertyCellType::kNoCell);
Handle<SeededNumberDictionary> new_dictionary =
SeededNumberDictionary::AddNumberEntry(dictionary, index, value,
details);
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());
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.
}
+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;
}
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);
// 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,
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,
};
}
static PropertyDetails Empty() {
- return PropertyDetails(NONE, DATA, 0, PropertyCellType::kInvalid);
+ return PropertyDetails(NONE, DATA, 0, PropertyCellType::kNoCell);
}
int pointer() const { return DescriptorPointer::decode(value_); }