Commit af4f1520 authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[turbofan] Introduce a new MapGuard operator.

The MapGuard node sits in the effect chain as a hint for other
optimization passes that a certain value has a certain (set of)
map(s) guarded by checks on the control chain. This is useful
to learn from explicit control flow inserted for polymorphic
property accesses, and then used as part of the polymorphic
inlining.

This change improves the score on the Octane/DeltaBlue benchmark
by around 7-8% and the score on the Octane/Richards benchmark by
like 3% on average.

Bug: v8:5267
Change-Id: Id0b0b2c72e6a9342d5750a0d62cf6be6fb8c5916
Also-By: jarin@chromium.org
Reviewed-on: https://chromium-review.googlesource.com/620586
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47417}
parent 0d51a259
......@@ -297,6 +297,11 @@ RegionObservability RegionObservabilityOf(Operator const* op) {
return OpParameter<RegionObservability>(op);
}
ZoneHandleSet<Map> MapGuardMapsOf(Operator const* op) {
DCHECK_EQ(IrOpcode::kMapGuard, op->opcode());
return OpParameter<ZoneHandleSet<Map>>(op);
}
Type* TypeGuardTypeOf(Operator const* op) {
DCHECK_EQ(IrOpcode::kTypeGuard, op->opcode());
return OpParameter<Type*>(op);
......@@ -1125,6 +1130,14 @@ const Operator* CommonOperatorBuilder::Phi(MachineRepresentation rep,
rep); // parameter
}
const Operator* CommonOperatorBuilder::MapGuard(ZoneHandleSet<Map> maps) {
return new (zone()) Operator1<ZoneHandleSet<Map>>( // --
IrOpcode::kMapGuard, Operator::kEliminatable, // opcode
"MapGuard", // name
1, 1, 1, 0, 1, 0, // counts
maps); // parameter
}
const Operator* CommonOperatorBuilder::TypeGuard(Type* type) {
return new (zone()) Operator1<Type*>( // --
IrOpcode::kTypeGuard, Operator::kPure, // opcode
......
......@@ -12,6 +12,7 @@
#include "src/globals.h"
#include "src/machine-type.h"
#include "src/zone/zone-containers.h"
#include "src/zone/zone-handle-set.h"
namespace v8 {
namespace internal {
......@@ -299,6 +300,8 @@ RegionObservability RegionObservabilityOf(Operator const*) WARN_UNUSED_RESULT;
std::ostream& operator<<(std::ostream& os,
const ZoneVector<MachineType>* types);
ZoneHandleSet<Map> MapGuardMapsOf(Operator const*) WARN_UNUSED_RESULT;
Type* TypeGuardTypeOf(Operator const*) WARN_UNUSED_RESULT;
int OsrValueIndexOf(Operator const*);
......@@ -392,6 +395,7 @@ class V8_EXPORT_PRIVATE CommonOperatorBuilder final
const Operator* TailCall(const CallDescriptor* descriptor);
const Operator* Projection(size_t index);
const Operator* Retain();
const Operator* MapGuard(ZoneHandleSet<Map> maps);
const Operator* TypeGuard(Type* type);
// Constructs a new merge or phi operator with the same opcode as {op}, but
......
......@@ -783,6 +783,10 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
// Perform map check on {receiver}.
MapHandles const& receiver_maps = access_info.receiver_maps();
{
// Whether to insert a dedicated MapGuard node into the
// 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());
......@@ -794,6 +798,11 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
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
// node already gives you all the information you need
// along the effect chain.
insert_map_guard = false;
} else {
for (auto map : receiver_maps) {
Node* check =
......@@ -816,6 +825,10 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
this_effects.push_back(receiverissmi_effect);
this_controls.push_back(receiverissmi_control);
receiverissmi_effect = receiverissmi_control = nullptr;
// The {receiver} can also be a Smi in this case, so
// a MapGuard doesn't make sense for this at all.
insert_map_guard = false;
}
// Create single chokepoint for the control.
......@@ -832,6 +845,16 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
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;
for (auto receiver_map : receiver_maps) {
maps.insert(receiver_map, graph()->zone());
}
this_effect = graph()->NewNode(common()->MapGuard(maps), receiver,
this_effect, this_control);
}
}
// Generate the actual property access.
......
......@@ -98,6 +98,8 @@ Reduction LoadElimination::Reduce(Node* node) {
switch (node->opcode()) {
case IrOpcode::kArrayBufferWasNeutered:
return ReduceArrayBufferWasNeutered(node);
case IrOpcode::kMapGuard:
return ReduceMapGuard(node);
case IrOpcode::kCheckMaps:
return ReduceCheckMaps(node);
case IrOpcode::kEnsureWritableFastElements:
......@@ -621,6 +623,22 @@ Reduction LoadElimination::ReduceArrayBufferWasNeutered(Node* node) {
return UpdateState(node, state);
}
Reduction LoadElimination::ReduceMapGuard(Node* node) {
ZoneHandleSet<Map> const maps = MapGuardMapsOf(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)) return Replace(effect);
state = state->KillMaps(object, zone());
// TODO(turbofan): Compute the intersection.
}
state = state->AddMaps(object, maps, zone());
return UpdateState(node, state);
}
Reduction LoadElimination::ReduceCheckMaps(Node* node) {
ZoneHandleSet<Map> const maps = CheckMapsParametersOf(node->op()).maps();
Node* const object = NodeProperties::GetValueInput(node, 0);
......
......@@ -269,6 +269,7 @@ class V8_EXPORT_PRIVATE LoadElimination final
Reduction ReduceArrayBufferWasNeutered(Node* node);
Reduction ReduceCheckMaps(Node* node);
Reduction ReduceMapGuard(Node* node);
Reduction ReduceEnsureWritableFastElements(Node* node);
Reduction ReduceMaybeGrowFastElements(Node* node);
Reduction ReduceTransitionElementsKind(Node* node);
......
......@@ -360,6 +360,14 @@ NodeProperties::InferReceiverMapsResult NodeProperties::InferReceiverMaps(
InferReceiverMapsResult result = kReliableReceiverMaps;
while (true) {
switch (effect->opcode()) {
case IrOpcode::kMapGuard: {
Node* const object = GetValueInput(effect, 0);
if (IsSame(receiver, object)) {
*maps_return = MapGuardMapsOf(effect->op());
return result;
}
break;
}
case IrOpcode::kCheckMaps: {
Node* const object = GetValueInput(effect, 0);
if (IsSame(receiver, object)) {
......
......@@ -72,6 +72,7 @@
V(LoopExitEffect) \
V(Projection) \
V(Retain) \
V(MapGuard) \
V(TypeGuard)
#define COMMON_OP_LIST(V) \
......
......@@ -2761,6 +2761,9 @@ class RepresentationSelector {
}
return;
}
case IrOpcode::kMapGuard:
// Eliminate MapGuard nodes here.
return VisitUnused(node);
case IrOpcode::kCheckMaps:
case IrOpcode::kTransitionElementsKind: {
VisitInputs(node);
......
......@@ -861,6 +861,8 @@ Type* Typer::Visitor::TypeProjection(Node* node) {
return Type::Any();
}
Type* Typer::Visitor::TypeMapGuard(Node* node) { UNREACHABLE(); }
Type* Typer::Visitor::TypeTypeGuard(Node* node) {
Type* const type = Operand(node, 0);
return typer_->operation_typer()->TypeTypeGuard(node->op(), type);
......
......@@ -1284,6 +1284,9 @@ void Verifier::Visitor::Check(Node* node) {
CheckValueInputIs(node, 0, Type::Number());
CheckTypeIs(node, Type::Number());
break;
case IrOpcode::kMapGuard:
CheckNotTyped(node);
break;
case IrOpcode::kTypeGuard:
CheckTypeIs(node, TypeGuardTypeOf(node->op()));
break;
......
......@@ -163,6 +163,15 @@ class ZoneHandleSet final {
intptr_t data_;
};
template <typename T>
std::ostream& operator<<(std::ostream& os, ZoneHandleSet<T> set) {
for (size_t i = 0; i < set.size(); ++i) {
if (i > 0) os << ", ";
os << set.at(i);
}
return os;
}
template <typename T>
class ZoneHandleSet<T>::const_iterator {
public:
......
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