[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/636365 Reviewed-by: Jaroslav Sevcik <jarin@chromium.org> Commit-Queue: Benedikt Meurer <bmeurer@chromium.org> Cr-Commit-Position: refs/heads/master@{#47635}
This commit is contained in:
parent
3dbc04f72f
commit
8f1a92ce71
@ -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());
|
||||
|
Loading…
Reference in New Issue
Block a user