Commit 8f1a92ce authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[turbofan] Introduce a dedicated CompareMaps operator.

Instead of introducing a lot of explicit branching in the
JSNativeContextSpecialization for polymorphic property accesses
that cannot be folded into a single LoadField/StoreField, and which
are mostly invisible and not optimizable for later passes, we now
have a single CompareMaps operator that takes a set of maps (like the
CheckMaps operator) and produces a boolean indicating the result of
the comparison.

R=jarin@chromium.org

Bug: v8:6761
Change-Id: Iee8788e915b762d542acb54feb9931346e442dc0
Reviewed-on: https://chromium-review.googlesource.com/636365Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47635}
parent 3dbc04f7
......@@ -629,6 +629,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kCheckMaps:
result = LowerCheckMaps(node, frame_state);
break;
case IrOpcode::kCompareMaps:
result = LowerCompareMaps(node);
break;
case IrOpcode::kCheckMapValue:
LowerCheckMapValue(node, frame_state);
break;
......@@ -1287,6 +1290,28 @@ Node* EffectControlLinearizer::LowerCheckMaps(Node* node, Node* frame_state) {
return value;
}
Node* EffectControlLinearizer::LowerCompareMaps(Node* node) {
ZoneHandleSet<Map> const& maps = CompareMapsParametersOf(node->op());
size_t const map_count = maps.size();
Node* value = node->InputAt(0);
auto done = __ MakeLabelFor(GraphAssemblerLabelType::kNonDeferred,
map_count + 1, MachineRepresentation::kBit);
// Load the current map of the {value}.
Node* value_map = __ LoadField(AccessBuilder::ForMap(), value);
for (size_t i = 0; i < map_count; ++i) {
Node* map = __ HeapConstant(maps[i]);
Node* check = __ WordEqual(value_map, map);
__ GotoIf(check, &done, __ Int32Constant(1));
}
__ Goto(&done, __ Int32Constant(0));
__ Bind(&done);
return done.PhiAt(0);
}
void EffectControlLinearizer::LowerCheckMapValue(Node* node,
Node* frame_state) {
Node* value = node->InputAt(0);
......
......@@ -55,6 +55,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
Node* LowerCheckBounds(Node* node, Node* frame_state);
Node* LowerCheckInternalizedString(Node* node, Node* frame_state);
Node* LowerCheckMaps(Node* node, Node* frame_state);
Node* LowerCompareMaps(Node* node);
void LowerCheckMapValue(Node* node, Node* frame_state);
Node* LowerCheckNumber(Node* node, Node* frame_state);
Node* LowerCheckReceiver(Node* node, Node* frame_state);
......
......@@ -765,12 +765,6 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
access_builder.BuildCheckHeapObject(receiver, &effect, control);
}
// Load the {receiver} map. The resulting effect is the dominating effect
// for all (polymorphic) branches.
Node* receiver_map = effect =
graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
receiver, effect, control);
// Generate code for the various different property access patterns.
Node* fallthrough_control = control;
for (size_t j = 0; j < access_infos.size(); ++j) {
......@@ -787,16 +781,12 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
// effect to be able to learn from the control flow.
bool insert_map_guard = true;
// Emit a (sequence of) map checks for other {receiver}s.
ZoneVector<Node*> this_controls(zone());
ZoneVector<Node*> this_effects(zone());
// Check maps for the {receiver}s.
if (j == access_infos.size() - 1) {
// Last map check on the fallthrough control path, do a
// conditional eager deoptimization exit here.
access_builder.BuildCheckMaps(receiver, &this_effect, this_control,
receiver_maps);
this_effects.push_back(this_effect);
this_controls.push_back(fallthrough_control);
fallthrough_control = nullptr;
// Don't insert a MapGuard in this case, as the CheckMaps
......@@ -804,17 +794,18 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
// along the effect chain.
insert_map_guard = false;
} else {
for (auto map : receiver_maps) {
Node* check =
graph()->NewNode(simplified()->ReferenceEqual(), receiver_map,
jsgraph()->Constant(map));
Node* branch = graph()->NewNode(common()->Branch(), check,
fallthrough_control);
fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
this_controls.push_back(
graph()->NewNode(common()->IfTrue(), branch));
this_effects.push_back(this_effect);
// Explicitly branch on the {receiver_maps}.
ZoneHandleSet<Map> maps;
for (Handle<Map> map : receiver_maps) {
maps.insert(map, graph()->zone());
}
Node* check = this_effect =
graph()->NewNode(simplified()->CompareMaps(maps), receiver,
this_effect, this_control);
Node* branch =
graph()->NewNode(common()->Branch(), check, this_control);
fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
this_control = graph()->NewNode(common()->IfTrue(), branch);
}
// The Number case requires special treatment to also deal with Smis.
......@@ -822,8 +813,10 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
// Join this check with the "receiver is smi" check above.
DCHECK_NOT_NULL(receiverissmi_effect);
DCHECK_NOT_NULL(receiverissmi_control);
this_effects.push_back(receiverissmi_effect);
this_controls.push_back(receiverissmi_control);
this_control = graph()->NewNode(common()->Merge(2), this_control,
receiverissmi_control);
this_effect = graph()->NewNode(common()->EffectPhi(2), this_effect,
receiverissmi_effect, this_control);
receiverissmi_effect = receiverissmi_control = nullptr;
// The {receiver} can also be a Smi in this case, so
......@@ -831,21 +824,6 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
insert_map_guard = false;
}
// Create single chokepoint for the control.
int const this_control_count = static_cast<int>(this_controls.size());
if (this_control_count == 1) {
this_control = this_controls.front();
this_effect = this_effects.front();
} else {
this_control =
graph()->NewNode(common()->Merge(this_control_count),
this_control_count, &this_controls.front());
this_effects.push_back(this_control);
this_effect =
graph()->NewNode(common()->EffectPhi(this_control_count),
this_control_count + 1, &this_effects.front());
}
// Introduce a MapGuard to learn from this on the effect chain.
if (insert_map_guard) {
ZoneHandleSet<Map> maps;
......
......@@ -102,6 +102,8 @@ Reduction LoadElimination::Reduce(Node* node) {
return ReduceMapGuard(node);
case IrOpcode::kCheckMaps:
return ReduceCheckMaps(node);
case IrOpcode::kCompareMaps:
return ReduceCompareMaps(node);
case IrOpcode::kEnsureWritableFastElements:
return ReduceEnsureWritableFastElements(node);
case IrOpcode::kMaybeGrowFastElements:
......@@ -695,6 +697,24 @@ Reduction LoadElimination::ReduceCheckMaps(Node* node) {
return UpdateState(node, state);
}
Reduction LoadElimination::ReduceCompareMaps(Node* node) {
ZoneHandleSet<Map> const maps = CompareMapsParametersOf(node->op());
Node* const object = NodeProperties::GetValueInput(node, 0);
Node* const effect = NodeProperties::GetEffectInput(node);
AbstractState const* state = node_states_.Get(effect);
if (state == nullptr) return NoChange();
ZoneHandleSet<Map> object_maps;
if (state->LookupMaps(object, &object_maps)) {
if (maps.contains(object_maps)) {
Node* value = jsgraph()->TrueConstant();
ReplaceWithValue(node, value, effect);
return Replace(value);
}
// TODO(turbofan): Compute the intersection.
}
return UpdateState(node, state);
}
Reduction LoadElimination::ReduceEnsureWritableFastElements(Node* node) {
Node* const object = NodeProperties::GetValueInput(node, 0);
Node* const elements = NodeProperties::GetValueInput(node, 1);
......
......@@ -266,6 +266,7 @@ class V8_EXPORT_PRIVATE LoadElimination final
Reduction ReduceArrayBufferWasNeutered(Node* node);
Reduction ReduceCheckMaps(Node* node);
Reduction ReduceCompareMaps(Node* node);
Reduction ReduceMapGuard(Node* node);
Reduction ReduceEnsureWritableFastElements(Node* node);
Reduction ReduceMaybeGrowFastElements(Node* node);
......
......@@ -339,6 +339,7 @@
V(CheckHeapObject) \
V(CheckFloat64Hole) \
V(CheckNotTaggedHole) \
V(CompareMaps) \
V(ConvertTaggedHoleToUndefined) \
V(Allocate) \
V(LoadField) \
......
......@@ -2770,6 +2770,9 @@ class RepresentationSelector {
VisitInputs(node);
return SetOutput(node, MachineRepresentation::kNone);
}
case IrOpcode::kCompareMaps:
return VisitUnop(node, UseInfo::AnyTagged(),
MachineRepresentation::kBit);
case IrOpcode::kEnsureWritableFastElements:
return VisitBinop(node, UseInfo::AnyTagged(),
MachineRepresentation::kTaggedPointer);
......
......@@ -8,6 +8,8 @@
#include "src/compiler/opcodes.h"
#include "src/compiler/operator.h"
#include "src/compiler/types.h"
#include "src/handles-inl.h"
#include "src/objects-inl.h"
#include "src/objects/map.h"
#include "src/objects/name.h"
......@@ -213,6 +215,11 @@ CheckMapsParameters const& CheckMapsParametersOf(Operator const* op) {
return OpParameter<CheckMapsParameters>(op);
}
ZoneHandleSet<Map> const& CompareMapsParametersOf(Operator const* op) {
DCHECK_EQ(IrOpcode::kCompareMaps, op->opcode());
return OpParameter<ZoneHandleSet<Map>>(op);
}
size_t hash_value(CheckTaggedInputMode mode) {
return static_cast<size_t>(mode);
}
......@@ -860,6 +867,16 @@ const Operator* SimplifiedOperatorBuilder::CheckMaps(CheckMapsFlags flags,
parameters); // parameter
}
const Operator* SimplifiedOperatorBuilder::CompareMaps(
ZoneHandleSet<Map> maps) {
return new (zone()) Operator1<ZoneHandleSet<Map>>( // --
IrOpcode::kCompareMaps, // opcode
Operator::kEliminatable, // flags
"CompareMaps", // name
1, 1, 1, 1, 1, 0, // counts
maps); // parameter
}
const Operator* SimplifiedOperatorBuilder::CheckFloat64Hole(
CheckFloat64HoleMode mode) {
switch (mode) {
......
......@@ -157,6 +157,10 @@ std::ostream& operator<<(std::ostream&, CheckMapsParameters const&);
CheckMapsParameters const& CheckMapsParametersOf(Operator const*)
WARN_UNUSED_RESULT;
// Parameters for CompareMaps operator.
ZoneHandleSet<Map> const& CompareMapsParametersOf(Operator const*)
WARN_UNUSED_RESULT;
// A descriptor for growing elements backing stores.
enum class GrowFastElementsFlag : uint8_t {
kNone = 0u,
......@@ -396,6 +400,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* CheckBounds();
const Operator* CheckMaps(CheckMapsFlags, ZoneHandleSet<Map>);
const Operator* CheckMapValue();
const Operator* CompareMaps(ZoneHandleSet<Map>);
const Operator* CheckHeapObject();
const Operator* CheckInternalizedString();
......
......@@ -1862,9 +1862,10 @@ Type* Typer::Visitor::TypeCheckMaps(Node* node) {
UNREACHABLE();
}
Type* Typer::Visitor::TypeCompareMaps(Node* node) { return Type::Boolean(); }
Type* Typer::Visitor::TypeCheckMapValue(Node* node) {
UNREACHABLE();
return nullptr;
}
Type* Typer::Visitor::TypeCheckNumber(Node* node) {
......
......@@ -1190,6 +1190,10 @@ void Verifier::Visitor::Check(Node* node) {
CheckValueInputIs(node, 0, Type::Any());
CheckNotTyped(node);
break;
case IrOpcode::kCompareMaps:
CheckValueInputIs(node, 0, Type::Any());
CheckTypeIs(node, Type::Boolean());
break;
case IrOpcode::kCheckMapValue:
CheckValueInputIs(node, 0, Type::Any());
CheckValueInputIs(node, 1, Type::Any());
......
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