[maglev] Optimize store to constant field

This doesn't optimize stores to object literals yet since the feedback
is partially broken.

Bug: v8:7700
Change-Id: Idfa95b51cb2673b47c6a626a6b60501838ffb468
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4023045
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Commit-Queue: Toon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/main@{#84238}
This commit is contained in:
Toon Verwaest 2022-11-14 13:11:11 +01:00 committed by V8 LUCI CQ
parent b9eeaf1b88
commit 6ae0cd3717
5 changed files with 111 additions and 53 deletions

View File

@ -1565,25 +1565,24 @@ bool MaglevGraphBuilder::TryFoldLoadDictPrototypeConstant(
return true; return true;
} }
bool MaglevGraphBuilder::TryFoldLoadConstantDataField( ValueNode* MaglevGraphBuilder::TryFoldLoadConstantDataField(
compiler::PropertyAccessInfo access_info, ValueNode* lookup_start_object) { compiler::PropertyAccessInfo access_info, ValueNode* lookup_start_object) {
if (!access_info.IsFastDataConstant()) return false; if (!access_info.IsFastDataConstant()) return nullptr;
base::Optional<compiler::JSObjectRef> source; base::Optional<compiler::JSObjectRef> source;
if (access_info.holder().has_value()) { if (access_info.holder().has_value()) {
source = access_info.holder(); source = access_info.holder();
} else if (Constant* n = lookup_start_object->TryCast<Constant>()) { } else if (Constant* n = lookup_start_object->TryCast<Constant>()) {
if (!n->ref().IsJSObject()) return false; if (!n->ref().IsJSObject()) return nullptr;
source = n->ref().AsJSObject(); source = n->ref().AsJSObject();
} else { } else {
return false; return nullptr;
} }
base::Optional<compiler::ObjectRef> constant = base::Optional<compiler::ObjectRef> constant =
source.value().GetOwnFastDataProperty(access_info.field_representation(), source.value().GetOwnFastDataProperty(access_info.field_representation(),
access_info.field_index(), access_info.field_index(),
broker()->dependencies()); broker()->dependencies());
if (!constant.has_value()) return false; if (!constant.has_value()) return nullptr;
SetAccumulator(GetConstant(constant.value())); return GetConstant(constant.value());
return true;
} }
bool MaglevGraphBuilder::TryBuildPropertyGetterCall( bool MaglevGraphBuilder::TryBuildPropertyGetterCall(
@ -1629,9 +1628,12 @@ bool MaglevGraphBuilder::TryBuildPropertySetterCall(
} }
} }
void MaglevGraphBuilder::BuildLoadField( ValueNode* MaglevGraphBuilder::BuildLoadField(
compiler::PropertyAccessInfo access_info, ValueNode* lookup_start_object) { compiler::PropertyAccessInfo access_info, ValueNode* lookup_start_object) {
if (TryFoldLoadConstantDataField(access_info, lookup_start_object)) return; if (ValueNode* result =
TryFoldLoadConstantDataField(access_info, lookup_start_object)) {
return result;
}
// Resolve property holder. // Resolve property holder.
ValueNode* load_source; ValueNode* load_source;
@ -1650,37 +1652,36 @@ void MaglevGraphBuilder::BuildLoadField(
// Do the load. // Do the load.
if (field_index.is_double()) { if (field_index.is_double()) {
SetAccumulator( return AddNewNode<LoadDoubleField>({load_source}, field_index.offset());
AddNewNode<LoadDoubleField>({load_source}, field_index.offset())); }
} else { ValueNode* value =
ValueNode* value = AddNewNode<LoadTaggedField>({load_source}, field_index.offset());
AddNewNode<LoadTaggedField>({load_source}, field_index.offset()); // Insert stable field information if present.
SetAccumulator(value); if (access_info.field_representation().IsSmi()) {
// Insert stable field information if present. NodeInfo* known_info = known_node_aspects().GetOrCreateInfoFor(value);
if (access_info.field_representation().IsSmi()) { known_info->type = NodeType::kSmi;
NodeInfo* known_info = known_node_aspects().GetOrCreateInfoFor(value); } else if (access_info.field_representation().IsHeapObject()) {
known_info->type = NodeType::kSmi; NodeInfo* known_info = known_node_aspects().GetOrCreateInfoFor(value);
} else if (access_info.field_representation().IsHeapObject()) { if (access_info.field_map().has_value() &&
NodeInfo* known_info = known_node_aspects().GetOrCreateInfoFor(value); access_info.field_map().value().is_stable()) {
if (access_info.field_map().has_value() && DCHECK(access_info.field_map().value().IsJSReceiverMap());
access_info.field_map().value().is_stable()) { known_info->type = NodeType::kJSReceiverWithKnownMap;
DCHECK(access_info.field_map().value().IsJSReceiverMap()); auto map = access_info.field_map().value();
known_info->type = NodeType::kJSReceiverWithKnownMap; ZoneHandleSet<Map> stable_maps(map.object());
auto map = access_info.field_map().value(); ZoneHandleSet<Map> unstable_maps;
ZoneHandleSet<Map> stable_maps(map.object()); known_node_aspects().stable_maps.emplace(value, stable_maps);
ZoneHandleSet<Map> unstable_maps; known_node_aspects().unstable_maps.emplace(value, unstable_maps);
known_node_aspects().stable_maps.emplace(value, stable_maps); broker()->dependencies()->DependOnStableMap(map);
known_node_aspects().unstable_maps.emplace(value, unstable_maps); } else {
broker()->dependencies()->DependOnStableMap(map); known_info->type = NodeType::kAnyHeapObject;
} else {
known_info->type = NodeType::kAnyHeapObject;
}
} }
} }
return value;
} }
bool MaglevGraphBuilder::TryBuildStoreField( bool MaglevGraphBuilder::TryBuildStoreField(
compiler::PropertyAccessInfo access_info, ValueNode* receiver) { compiler::PropertyAccessInfo access_info, ValueNode* receiver,
compiler::AccessMode access_mode) {
FieldIndex field_index = access_info.field_index(); FieldIndex field_index = access_info.field_index();
Representation field_representation = access_info.field_representation(); Representation field_representation = access_info.field_representation();
@ -1688,9 +1689,25 @@ bool MaglevGraphBuilder::TryBuildStoreField(
compiler::MapRef transition = access_info.transition_map().value(); compiler::MapRef transition = access_info.transition_map().value();
compiler::MapRef original_map = transition.GetBackPointer().AsMap(); compiler::MapRef original_map = transition.GetBackPointer().AsMap();
// TODO(verwaest): Support growing backing stores. // TODO(verwaest): Support growing backing stores.
if (original_map.UnusedPropertyFields() == 0) return false; if (original_map.UnusedPropertyFields() == 0) {
} else if (access_info.IsFastDataConstant()) { return false;
return false; }
} else if (access_info.IsFastDataConstant() &&
access_mode != compiler::AccessMode::kStoreInLiteral) {
// For object literals we want to just store, similar to StoreInLiteral, but
// Define is also used for classes so we can't. Bail out for now.
if (access_mode == compiler::AccessMode::kDefine) return false;
// TODO(verwaest): Support doubles.
if (field_representation.IsDouble()) return false;
ValueNode* value = GetAccumulatorTagged();
ValueNode* expected = BuildLoadField(access_info, receiver);
if (Constant* constant = expected->TryCast<Constant>()) {
BuildCheckValue(value, constant->ref());
} else {
AddNewNode<CheckDynamicValue>({value, expected});
}
return true;
} }
ValueNode* store_target; ValueNode* store_target;
@ -1781,7 +1798,7 @@ bool MaglevGraphBuilder::TryBuildPropertyLoad(
return true; return true;
case compiler::PropertyAccessInfo::kDataField: case compiler::PropertyAccessInfo::kDataField:
case compiler::PropertyAccessInfo::kFastDataConstant: case compiler::PropertyAccessInfo::kFastDataConstant:
BuildLoadField(access_info, lookup_start_object); SetAccumulator(BuildLoadField(access_info, lookup_start_object));
RecordKnownProperty(lookup_start_object, name, RecordKnownProperty(lookup_start_object, name,
current_interpreter_frame_.accumulator(), current_interpreter_frame_.accumulator(),
access_info.IsFastDataConstant()); access_info.IsFastDataConstant());
@ -1808,7 +1825,8 @@ bool MaglevGraphBuilder::TryBuildPropertyLoad(
bool MaglevGraphBuilder::TryBuildPropertyStore( bool MaglevGraphBuilder::TryBuildPropertyStore(
ValueNode* receiver, compiler::NameRef name, ValueNode* receiver, compiler::NameRef name,
compiler::PropertyAccessInfo const& access_info) { compiler::PropertyAccessInfo const& access_info,
compiler::AccessMode access_mode) {
if (access_info.holder().has_value()) { if (access_info.holder().has_value()) {
broker()->dependencies()->DependOnStablePrototypeChains( broker()->dependencies()->DependOnStablePrototypeChains(
access_info.lookup_start_object_maps(), kStartAtPrototype, access_info.lookup_start_object_maps(), kStartAtPrototype,
@ -1820,7 +1838,7 @@ bool MaglevGraphBuilder::TryBuildPropertyStore(
GetAccumulatorTagged()); GetAccumulatorTagged());
} else { } else {
DCHECK(access_info.IsDataField() || access_info.IsFastDataConstant()); DCHECK(access_info.IsDataField() || access_info.IsFastDataConstant());
if (TryBuildStoreField(access_info, receiver)) { if (TryBuildStoreField(access_info, receiver, access_mode)) {
RecordKnownProperty(receiver, name, RecordKnownProperty(receiver, name,
current_interpreter_frame_.accumulator(), current_interpreter_frame_.accumulator(),
access_info.IsFastDataConstant()); access_info.IsFastDataConstant());
@ -1842,7 +1860,7 @@ bool MaglevGraphBuilder::TryBuildPropertyAccess(
case compiler::AccessMode::kStoreInLiteral: case compiler::AccessMode::kStoreInLiteral:
case compiler::AccessMode::kDefine: case compiler::AccessMode::kDefine:
DCHECK_EQ(receiver, lookup_start_object); DCHECK_EQ(receiver, lookup_start_object);
return TryBuildPropertyStore(receiver, name, access_info); return TryBuildPropertyStore(receiver, name, access_info, access_mode);
case compiler::AccessMode::kHas: case compiler::AccessMode::kHas:
// TODO(victorgomes): BuildPropertyTest. // TODO(victorgomes): BuildPropertyTest.
return false; return false;
@ -2922,7 +2940,7 @@ Call* MaglevGraphBuilder::BuildGenericCall(
} }
bool MaglevGraphBuilder::BuildCheckValue(ValueNode* node, bool MaglevGraphBuilder::BuildCheckValue(ValueNode* node,
const compiler::HeapObjectRef& ref) { const compiler::ObjectRef& ref) {
if (node->Is<Constant>()) { if (node->Is<Constant>()) {
if (node->Cast<Constant>()->object().equals(ref)) return true; if (node->Cast<Constant>()->object().equals(ref)) return true;
EmitUnconditionalDeopt(DeoptimizeReason::kUnknown); EmitUnconditionalDeopt(DeoptimizeReason::kUnknown);

View File

@ -1117,7 +1117,7 @@ class MaglevGraphBuilder {
ZoneVector<compiler::MapRef> const& maps); ZoneVector<compiler::MapRef> const& maps);
// Emits an unconditional deopt and returns false if the node is a constant // Emits an unconditional deopt and returns false if the node is a constant
// that doesn't match the ref. // that doesn't match the ref.
bool BuildCheckValue(ValueNode* node, const compiler::HeapObjectRef& ref); bool BuildCheckValue(ValueNode* node, const compiler::ObjectRef& ref);
ValueNode* GetInt32ElementIndex(interpreter::Register reg) { ValueNode* GetInt32ElementIndex(interpreter::Register reg) {
ValueNode* index_object = current_interpreter_frame_.get(reg); ValueNode* index_object = current_interpreter_frame_.get(reg);
@ -1130,13 +1130,16 @@ class MaglevGraphBuilder {
bool TryFoldLoadDictPrototypeConstant( bool TryFoldLoadDictPrototypeConstant(
compiler::PropertyAccessInfo access_info); compiler::PropertyAccessInfo access_info);
bool TryFoldLoadConstantDataField(compiler::PropertyAccessInfo access_info, // Returns a ValueNode if the load could be folded, and nullptr otherwise.
ValueNode* lookup_start_object); ValueNode* TryFoldLoadConstantDataField(
compiler::PropertyAccessInfo access_info, ValueNode* lookup_start_object);
void BuildLoadField(compiler::PropertyAccessInfo access_info, // Returns the loaded value node but doesn't update the accumulator yet.
ValueNode* lookup_start_object); ValueNode* BuildLoadField(compiler::PropertyAccessInfo access_info,
ValueNode* lookup_start_object);
bool TryBuildStoreField(compiler::PropertyAccessInfo access_info, bool TryBuildStoreField(compiler::PropertyAccessInfo access_info,
ValueNode* receiver); ValueNode* receiver,
compiler::AccessMode access_mode);
bool TryBuildPropertyGetterCall(compiler::PropertyAccessInfo access_info, bool TryBuildPropertyGetterCall(compiler::PropertyAccessInfo access_info,
ValueNode* receiver, ValueNode* receiver,
ValueNode* lookup_start_object); ValueNode* lookup_start_object);
@ -1147,7 +1150,8 @@ class MaglevGraphBuilder {
compiler::NameRef name, compiler::NameRef name,
compiler::PropertyAccessInfo const& access_info); compiler::PropertyAccessInfo const& access_info);
bool TryBuildPropertyStore(ValueNode* receiver, compiler::NameRef name, bool TryBuildPropertyStore(ValueNode* receiver, compiler::NameRef name,
compiler::PropertyAccessInfo const& access_info); compiler::PropertyAccessInfo const& access_info,
compiler::AccessMode access_mode);
bool TryBuildPropertyAccess(ValueNode* receiver, bool TryBuildPropertyAccess(ValueNode* receiver,
ValueNode* lookup_start_object, ValueNode* lookup_start_object,
compiler::NameRef name, compiler::NameRef name,

View File

@ -199,6 +199,7 @@ class MaglevGraphVerifier {
DCHECK_EQ(node->input_count(), 1); DCHECK_EQ(node->input_count(), 1);
CheckValueInputIs(node, 0, ValueRepresentation::kFloat64); CheckValueInputIs(node, 0, ValueRepresentation::kFloat64);
break; break;
case Opcode::kCheckDynamicValue:
case Opcode::kForInPrepare: case Opcode::kForInPrepare:
case Opcode::kGenericAdd: case Opcode::kGenericAdd:
case Opcode::kGenericBitwiseAnd: case Opcode::kGenericBitwiseAnd:

View File

@ -1159,6 +1159,22 @@ void CheckValue::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const { MaglevGraphLabeller* graph_labeller) const {
os << "(" << *value().object() << ")"; os << "(" << *value().object() << ")";
} }
void CheckDynamicValue::AllocateVreg(MaglevVregAllocationState* vreg_state) {
UseRegister(first_input());
UseRegister(second_input());
}
void CheckDynamicValue::GenerateCode(MaglevAssembler* masm,
const ProcessingState& state) {
Register first = ToRegister(first_input());
Register second = ToRegister(second_input());
__ cmpl(first, second);
__ EmitEagerDeoptIf(not_equal, DeoptimizeReason::kWrongValue, this);
}
void CheckDynamicValue::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
}
void CheckSmi::AllocateVreg(MaglevVregAllocationState* vreg_state) { void CheckSmi::AllocateVreg(MaglevVregAllocationState* vreg_state) {
UseRegister(receiver_input()); UseRegister(receiver_input());
} }

View File

@ -213,6 +213,7 @@ class CompactInterpreterFrameState;
#define NODE_LIST(V) \ #define NODE_LIST(V) \
V(AssertInt32) \ V(AssertInt32) \
V(CheckDynamicValue) \
V(CheckInt32IsSmi) \ V(CheckInt32IsSmi) \
V(CheckUint32IsSmi) \ V(CheckUint32IsSmi) \
V(CheckHeapObject) \ V(CheckHeapObject) \
@ -3085,12 +3086,12 @@ class CheckValue : public FixedInputNodeT<1, CheckValue> {
using Base = FixedInputNodeT<1, CheckValue>; using Base = FixedInputNodeT<1, CheckValue>;
public: public:
explicit CheckValue(uint64_t bitfield, const compiler::HeapObjectRef& value) explicit CheckValue(uint64_t bitfield, const compiler::ObjectRef& value)
: Base(bitfield), value_(value) {} : Base(bitfield), value_(value) {}
static constexpr OpProperties kProperties = OpProperties::EagerDeopt(); static constexpr OpProperties kProperties = OpProperties::EagerDeopt();
compiler::HeapObjectRef value() const { return value_; } compiler::ObjectRef value() const { return value_; }
static constexpr int kTargetIndex = 0; static constexpr int kTargetIndex = 0;
Input& target_input() { return input(kTargetIndex); } Input& target_input() { return input(kTargetIndex); }
@ -3100,7 +3101,25 @@ class CheckValue : public FixedInputNodeT<1, CheckValue> {
void PrintParams(std::ostream&, MaglevGraphLabeller*) const; void PrintParams(std::ostream&, MaglevGraphLabeller*) const;
private: private:
const compiler::HeapObjectRef value_; const compiler::ObjectRef value_;
};
class CheckDynamicValue : public FixedInputNodeT<2, CheckDynamicValue> {
using Base = FixedInputNodeT<2, CheckDynamicValue>;
public:
explicit CheckDynamicValue(uint64_t bitfield) : Base(bitfield) {}
static constexpr OpProperties kProperties = OpProperties::EagerDeopt();
static constexpr int kFirstIndex = 0;
static constexpr int kSecondIndex = 1;
Input& first_input() { return input(kFirstIndex); }
Input& second_input() { return input(kSecondIndex); }
void AllocateVreg(MaglevVregAllocationState*);
void GenerateCode(MaglevAssembler*, const ProcessingState&);
void PrintParams(std::ostream&, MaglevGraphLabeller*) const;
}; };
class CheckSmi : public FixedInputNodeT<1, CheckSmi> { class CheckSmi : public FixedInputNodeT<1, CheckSmi> {