[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;
}
bool MaglevGraphBuilder::TryFoldLoadConstantDataField(
ValueNode* MaglevGraphBuilder::TryFoldLoadConstantDataField(
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;
if (access_info.holder().has_value()) {
source = access_info.holder();
} 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();
} else {
return false;
return nullptr;
}
base::Optional<compiler::ObjectRef> constant =
source.value().GetOwnFastDataProperty(access_info.field_representation(),
access_info.field_index(),
broker()->dependencies());
if (!constant.has_value()) return false;
SetAccumulator(GetConstant(constant.value()));
return true;
if (!constant.has_value()) return nullptr;
return GetConstant(constant.value());
}
bool MaglevGraphBuilder::TryBuildPropertyGetterCall(
@ -1629,9 +1628,12 @@ bool MaglevGraphBuilder::TryBuildPropertySetterCall(
}
}
void MaglevGraphBuilder::BuildLoadField(
ValueNode* MaglevGraphBuilder::BuildLoadField(
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.
ValueNode* load_source;
@ -1650,37 +1652,36 @@ void MaglevGraphBuilder::BuildLoadField(
// Do the load.
if (field_index.is_double()) {
SetAccumulator(
AddNewNode<LoadDoubleField>({load_source}, field_index.offset()));
} else {
ValueNode* value =
AddNewNode<LoadTaggedField>({load_source}, field_index.offset());
SetAccumulator(value);
// Insert stable field information if present.
if (access_info.field_representation().IsSmi()) {
NodeInfo* known_info = known_node_aspects().GetOrCreateInfoFor(value);
known_info->type = NodeType::kSmi;
} else if (access_info.field_representation().IsHeapObject()) {
NodeInfo* known_info = known_node_aspects().GetOrCreateInfoFor(value);
if (access_info.field_map().has_value() &&
access_info.field_map().value().is_stable()) {
DCHECK(access_info.field_map().value().IsJSReceiverMap());
known_info->type = NodeType::kJSReceiverWithKnownMap;
auto map = access_info.field_map().value();
ZoneHandleSet<Map> stable_maps(map.object());
ZoneHandleSet<Map> unstable_maps;
known_node_aspects().stable_maps.emplace(value, stable_maps);
known_node_aspects().unstable_maps.emplace(value, unstable_maps);
broker()->dependencies()->DependOnStableMap(map);
} else {
known_info->type = NodeType::kAnyHeapObject;
}
return AddNewNode<LoadDoubleField>({load_source}, field_index.offset());
}
ValueNode* value =
AddNewNode<LoadTaggedField>({load_source}, field_index.offset());
// Insert stable field information if present.
if (access_info.field_representation().IsSmi()) {
NodeInfo* known_info = known_node_aspects().GetOrCreateInfoFor(value);
known_info->type = NodeType::kSmi;
} else if (access_info.field_representation().IsHeapObject()) {
NodeInfo* known_info = known_node_aspects().GetOrCreateInfoFor(value);
if (access_info.field_map().has_value() &&
access_info.field_map().value().is_stable()) {
DCHECK(access_info.field_map().value().IsJSReceiverMap());
known_info->type = NodeType::kJSReceiverWithKnownMap;
auto map = access_info.field_map().value();
ZoneHandleSet<Map> stable_maps(map.object());
ZoneHandleSet<Map> unstable_maps;
known_node_aspects().stable_maps.emplace(value, stable_maps);
known_node_aspects().unstable_maps.emplace(value, unstable_maps);
broker()->dependencies()->DependOnStableMap(map);
} else {
known_info->type = NodeType::kAnyHeapObject;
}
}
return value;
}
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();
Representation field_representation = access_info.field_representation();
@ -1688,9 +1689,25 @@ bool MaglevGraphBuilder::TryBuildStoreField(
compiler::MapRef transition = access_info.transition_map().value();
compiler::MapRef original_map = transition.GetBackPointer().AsMap();
// TODO(verwaest): Support growing backing stores.
if (original_map.UnusedPropertyFields() == 0) return false;
} else if (access_info.IsFastDataConstant()) {
return false;
if (original_map.UnusedPropertyFields() == 0) {
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;
@ -1781,7 +1798,7 @@ bool MaglevGraphBuilder::TryBuildPropertyLoad(
return true;
case compiler::PropertyAccessInfo::kDataField:
case compiler::PropertyAccessInfo::kFastDataConstant:
BuildLoadField(access_info, lookup_start_object);
SetAccumulator(BuildLoadField(access_info, lookup_start_object));
RecordKnownProperty(lookup_start_object, name,
current_interpreter_frame_.accumulator(),
access_info.IsFastDataConstant());
@ -1808,7 +1825,8 @@ bool MaglevGraphBuilder::TryBuildPropertyLoad(
bool MaglevGraphBuilder::TryBuildPropertyStore(
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()) {
broker()->dependencies()->DependOnStablePrototypeChains(
access_info.lookup_start_object_maps(), kStartAtPrototype,
@ -1820,7 +1838,7 @@ bool MaglevGraphBuilder::TryBuildPropertyStore(
GetAccumulatorTagged());
} else {
DCHECK(access_info.IsDataField() || access_info.IsFastDataConstant());
if (TryBuildStoreField(access_info, receiver)) {
if (TryBuildStoreField(access_info, receiver, access_mode)) {
RecordKnownProperty(receiver, name,
current_interpreter_frame_.accumulator(),
access_info.IsFastDataConstant());
@ -1842,7 +1860,7 @@ bool MaglevGraphBuilder::TryBuildPropertyAccess(
case compiler::AccessMode::kStoreInLiteral:
case compiler::AccessMode::kDefine:
DCHECK_EQ(receiver, lookup_start_object);
return TryBuildPropertyStore(receiver, name, access_info);
return TryBuildPropertyStore(receiver, name, access_info, access_mode);
case compiler::AccessMode::kHas:
// TODO(victorgomes): BuildPropertyTest.
return false;
@ -2922,7 +2940,7 @@ Call* MaglevGraphBuilder::BuildGenericCall(
}
bool MaglevGraphBuilder::BuildCheckValue(ValueNode* node,
const compiler::HeapObjectRef& ref) {
const compiler::ObjectRef& ref) {
if (node->Is<Constant>()) {
if (node->Cast<Constant>()->object().equals(ref)) return true;
EmitUnconditionalDeopt(DeoptimizeReason::kUnknown);

View File

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

View File

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

View File

@ -1159,6 +1159,22 @@ void CheckValue::PrintParams(std::ostream& os,
MaglevGraphLabeller* graph_labeller) const {
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) {
UseRegister(receiver_input());
}

View File

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