Commit 8201579e authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Introduce a dedicated CheckMaps simplified operator.

So far we always create explicit control flow for map checks during
JSNativeContextSpecialization, or in the monomorphic case we used a
CheckIf combined with a map comparison. In either case we cannot
currently effectively utilize the map check information during load
elimination to optimize (polymorphic) map checks and elements kind
transitions.

With the introduction of CheckMaps, we can now start optimizing map
checks in a more effective fashion. This CL doesn't change anything
in that direction yet, but merely changes the fundamental mechanism.

This also removes the stable map optimization from the Typer, where
it was always a bit odd, and puts it into the typed lowering and
the native context specialization instead.

R=epertoso@chromium.org
BUG=v8:4930,v8:5141

Review-Url: https://codereview.chromium.org/2196653002
Cr-Commit-Position: refs/heads/master@{#38166}
parent 45d6909e
......@@ -622,6 +622,9 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kCheckBounds:
state = LowerCheckBounds(node, frame_state, *effect, *control);
break;
case IrOpcode::kCheckMaps:
state = LowerCheckMaps(node, frame_state, *effect, *control);
break;
case IrOpcode::kCheckNumber:
state = LowerCheckNumber(node, frame_state, *effect, *control);
break;
......@@ -1022,6 +1025,43 @@ EffectControlLinearizer::LowerCheckBounds(Node* node, Node* frame_state,
return ValueEffectControl(index, effect, control);
}
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerCheckMaps(Node* node, Node* frame_state,
Node* effect, Node* control) {
Node* value = node->InputAt(0);
// Load the current map of the {value}.
Node* value_map = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForMap()), value, effect, control);
int const map_count = node->op()->ValueInputCount() - 1;
Node** controls = temp_zone()->NewArray<Node*>(map_count);
Node** effects = temp_zone()->NewArray<Node*>(map_count + 1);
for (int i = 0; i < map_count; ++i) {
Node* map = node->InputAt(1 + i);
Node* check = graph()->NewNode(machine()->WordEqual(), value_map, map);
if (i == map_count - 1) {
controls[i] = effects[i] = graph()->NewNode(
common()->DeoptimizeUnless(DeoptimizeReason::kWrongMap), check,
frame_state, effect, control);
} else {
control = graph()->NewNode(common()->Branch(), check, control);
controls[i] = graph()->NewNode(common()->IfTrue(), control);
control = graph()->NewNode(common()->IfFalse(), control);
effects[i] = effect;
}
}
control = graph()->NewNode(common()->Merge(map_count), map_count, controls);
effects[map_count] = control;
effect =
graph()->NewNode(common()->EffectPhi(map_count), map_count + 1, effects);
return ValueEffectControl(value, effect, control);
}
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerCheckNumber(Node* node, Node* frame_state,
Node* effect, Node* control) {
......
......@@ -63,6 +63,8 @@ class EffectControlLinearizer {
Node* control);
ValueEffectControl LowerCheckBounds(Node* node, Node* frame_state,
Node* effect, Node* control);
ValueEffectControl LowerCheckMaps(Node* node, Node* frame_state, Node* effect,
Node* control);
ValueEffectControl LowerCheckNumber(Node* node, Node* frame_state,
Node* effect, Node* control);
ValueEffectControl LowerCheckString(Node* node, Node* frame_state,
......
......@@ -114,6 +114,13 @@ class JSNativeContextSpecialization final : public AdvancedReducer {
ElementAccessInfo const& access_info,
AccessMode access_mode);
// Construct an appropriate map check.
Node* BuildCheckMaps(Node* receiver, Node* effect, Node* control,
std::vector<Handle<Map>> const& maps);
// Construct an appropriate heap object check.
Node* BuildCheckTaggedPointer(Node* receiver, Node* effect, Node* control);
// Adds stability dependencies on all prototypes of every class in
// {receiver_type} up to (and including) the {holder}.
void AssumePrototypesStable(std::vector<Handle<Map>> const& receiver_maps,
......
......@@ -1889,6 +1889,54 @@ Reduction JSTypedLowering::ReduceSelect(Node* node) {
return NoChange();
}
namespace {
MaybeHandle<Map> GetStableMapFromObjectType(Type* object_type) {
if (object_type->IsConstant() &&
object_type->AsConstant()->Value()->IsHeapObject()) {
Handle<Map> object_map(
Handle<HeapObject>::cast(object_type->AsConstant()->Value())->map());
if (object_map->is_stable()) return object_map;
} else if (object_type->IsClass()) {
Handle<Map> object_map = object_type->AsClass()->Map();
if (object_map->is_stable()) return object_map;
}
return MaybeHandle<Map>();
}
} // namespace
Reduction JSTypedLowering::ReduceCheckMaps(Node* node) {
// TODO(bmeurer): Find a better home for this thing!
// The CheckMaps(o, ...map...) can be eliminated if map is stable and
// either
// (a) o has type Constant(object) and map == object->map, or
// (b) o has type Class(map),
// and either
// (1) map cannot transition further, or
// (2) we can add a code dependency on the stability of map
// (to guard the Constant type information).
Node* const object = NodeProperties::GetValueInput(node, 0);
Type* const object_type = NodeProperties::GetType(object);
Node* const effect = NodeProperties::GetEffectInput(node);
Handle<Map> object_map;
if (GetStableMapFromObjectType(object_type).ToHandle(&object_map)) {
for (int i = 1; i < node->op()->ValueInputCount(); ++i) {
Node* const map = NodeProperties::GetValueInput(node, i);
Type* const map_type = NodeProperties::GetType(map);
if (map_type->IsConstant() &&
map_type->AsConstant()->Value().is_identical_to(object_map)) {
if (object_map->CanTransition()) {
DCHECK(flags() & kDeoptimizationEnabled);
dependencies()->AssumeMapStable(object_map);
}
return Replace(effect);
}
}
}
return NoChange();
}
Reduction JSTypedLowering::ReduceCheckString(Node* node) {
// TODO(bmeurer): Find a better home for this thing!
Node* const input = NodeProperties::GetValueInput(node, 0);
......@@ -1900,6 +1948,37 @@ Reduction JSTypedLowering::ReduceCheckString(Node* node) {
return NoChange();
}
Reduction JSTypedLowering::ReduceLoadField(Node* node) {
// TODO(bmeurer): Find a better home for this thing!
Node* const object = NodeProperties::GetValueInput(node, 0);
Type* const object_type = NodeProperties::GetType(object);
FieldAccess const& access = FieldAccessOf(node->op());
if (access.base_is_tagged == kTaggedBase &&
access.offset == HeapObject::kMapOffset) {
// We can replace LoadField[Map](o) with map if is stable and either
// (a) o has type Constant(object) and map == object->map, or
// (b) o has type Class(map),
// and either
// (1) map cannot transition further, or
// (2) deoptimization is enabled and we can add a code dependency on the
// stability of map (to guard the Constant type information).
Handle<Map> object_map;
if (GetStableMapFromObjectType(object_type).ToHandle(&object_map)) {
if (object_map->CanTransition()) {
if (flags() & kDeoptimizationEnabled) {
dependencies()->AssumeMapStable(object_map);
} else {
return NoChange();
}
}
Node* const value = jsgraph()->HeapConstant(object_map);
ReplaceWithValue(node, value);
return Replace(value);
}
}
return NoChange();
}
Reduction JSTypedLowering::ReduceNumberRoundop(Node* node) {
// TODO(bmeurer): Find a better home for this thing!
Node* const input = NodeProperties::GetValueInput(node, 0);
......@@ -2030,6 +2109,8 @@ Reduction JSTypedLowering::Reduce(Node* node) {
return ReduceJSGeneratorRestoreRegister(node);
case IrOpcode::kSelect:
return ReduceSelect(node);
case IrOpcode::kCheckMaps:
return ReduceCheckMaps(node);
case IrOpcode::kCheckString:
return ReduceCheckString(node);
case IrOpcode::kNumberCeil:
......@@ -2037,6 +2118,8 @@ Reduction JSTypedLowering::Reduce(Node* node) {
case IrOpcode::kNumberRound:
case IrOpcode::kNumberTrunc:
return ReduceNumberRoundop(node);
case IrOpcode::kLoadField:
return ReduceLoadField(node);
default:
break;
}
......
......@@ -78,7 +78,9 @@ class JSTypedLowering final : public AdvancedReducer {
Reduction ReduceJSGeneratorStore(Node* node);
Reduction ReduceJSGeneratorRestoreContinuation(Node* node);
Reduction ReduceJSGeneratorRestoreRegister(Node* node);
Reduction ReduceCheckMaps(Node* node);
Reduction ReduceCheckString(Node* node);
Reduction ReduceLoadField(Node* node);
Reduction ReduceNumberRoundop(Node* node);
Reduction ReduceSelect(Node* node);
Reduction ReduceJSSubtract(Node* node);
......
......@@ -51,6 +51,10 @@ bool MustAlias(Node* a, Node* b) { return QueryAlias(a, b) == kMustAlias; }
Reduction LoadElimination::Reduce(Node* node) {
switch (node->opcode()) {
case IrOpcode::kCheckMaps:
return ReduceCheckMaps(node);
case IrOpcode::kTransitionElementsKind:
return ReduceTransitionElementsKind(node);
case IrOpcode::kLoadField:
return ReduceLoadField(node);
case IrOpcode::kStoreField:
......@@ -301,6 +305,52 @@ void LoadElimination::AbstractStateForEffectNodes::Set(
info_for_node_[id] = state;
}
Reduction LoadElimination::ReduceCheckMaps(Node* node) {
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();
int const map_input_count = node->op()->ValueInputCount() - 1;
if (Node* const object_map = state->LookupField(object, 0)) {
for (int i = 0; i < map_input_count; ++i) {
Node* map = NodeProperties::GetValueInput(node, 1 + i);
if (map == object_map) return Replace(effect);
}
}
if (map_input_count == 1) {
Node* const map0 = NodeProperties::GetValueInput(node, 1);
state = state->AddField(object, 0, map0, zone());
}
return UpdateState(node, state);
}
Reduction LoadElimination::ReduceTransitionElementsKind(Node* node) {
Node* const object = NodeProperties::GetValueInput(node, 0);
Node* const source_map = NodeProperties::GetValueInput(node, 1);
Node* const target_map = NodeProperties::GetValueInput(node, 2);
Node* const effect = NodeProperties::GetEffectInput(node);
AbstractState const* state = node_states_.Get(effect);
if (state == nullptr) return NoChange();
if (Node* const object_map = state->LookupField(object, 0)) {
state = state->KillField(object, 0, zone());
if (source_map == object_map) {
state = state->AddField(object, 0, target_map, zone());
}
} else {
state = state->KillField(object, 0, zone());
}
ElementsTransition transition = ElementsTransitionOf(node->op());
switch (transition) {
case ElementsTransition::kFastTransition:
break;
case ElementsTransition::kSlowTransition:
// Kill the elements as well.
state = state->KillField(object, 2, zone());
break;
}
return UpdateState(node, state);
}
Reduction LoadElimination::ReduceLoadField(Node* node) {
FieldAccess const& access = FieldAccessOf(node->op());
Node* const object = NodeProperties::GetValueInput(node, 0);
......
......@@ -149,6 +149,8 @@ class LoadElimination final : public AdvancedReducer {
ZoneVector<AbstractState const*> info_for_node_;
};
Reduction ReduceCheckMaps(Node* node);
Reduction ReduceTransitionElementsKind(Node* node);
Reduction ReduceLoadField(Node* node);
Reduction ReduceStoreField(Node* node);
Reduction ReduceLoadElement(Node* node);
......
......@@ -269,6 +269,7 @@
V(StringFromCharCode) \
V(CheckBounds) \
V(CheckIf) \
V(CheckMaps) \
V(CheckNumber) \
V(CheckString) \
V(CheckTaggedPointer) \
......
......@@ -1469,10 +1469,7 @@ bool PipelineImpl::CreateGraph() {
// Type the graph and keep the Typer running on newly created nodes within
// this scope; the Typer is automatically unlinked from the Graph once we
// leave this scope below.
Typer typer(isolate(), data->graph(), info()->is_deoptimization_enabled()
? Typer::kDeoptimizationEnabled
: Typer::kNoFlags,
info()->dependencies());
Typer typer(isolate(), data->graph());
Run<TyperPhase>(&typer);
RunPrintAndVerify("Typed");
......
......@@ -2226,6 +2226,7 @@ class RepresentationSelector {
}
return;
}
case IrOpcode::kCheckMaps:
case IrOpcode::kTransitionElementsKind: {
VisitInputs(node);
return SetOutput(node, MachineRepresentation::kNone);
......
......@@ -507,6 +507,18 @@ const Operator* SimplifiedOperatorBuilder::CheckedInt32Mul(
return nullptr;
}
const Operator* SimplifiedOperatorBuilder::CheckMaps(int map_input_count) {
// TODO(bmeurer): Cache the most important versions of this operator.
DCHECK_LT(0, map_input_count);
int const value_input_count = 1 + map_input_count;
return new (zone()) Operator1<int>( // --
IrOpcode::kCheckMaps, // opcode
Operator::kNoThrow | Operator::kNoWrite, // flags
"CheckMaps", // name
value_input_count, 1, 1, 0, 1, 0, // counts
map_input_count); // parameter
}
const Operator* SimplifiedOperatorBuilder::CheckFloat64Hole(
CheckFloat64HoleMode mode) {
switch (mode) {
......
......@@ -273,6 +273,7 @@ class SimplifiedOperatorBuilder final : public ZoneObject {
const Operator* CheckIf();
const Operator* CheckBounds();
const Operator* CheckMaps(int map_input_count);
const Operator* CheckNumber();
const Operator* CheckString();
const Operator* CheckTaggedPointer();
......
......@@ -6,7 +6,6 @@
#include "src/base/flags.h"
#include "src/bootstrapper.h"
#include "src/compilation-dependencies.h"
#include "src/compiler/common-operator.h"
#include "src/compiler/graph-reducer.h"
#include "src/compiler/js-operator.h"
......@@ -31,12 +30,9 @@ class Typer::Decorator final : public GraphDecorator {
Typer* const typer_;
};
Typer::Typer(Isolate* isolate, Graph* graph, Flags flags,
CompilationDependencies* dependencies)
Typer::Typer(Isolate* isolate, Graph* graph)
: isolate_(isolate),
graph_(graph),
flags_(flags),
dependencies_(dependencies),
decorator_(nullptr),
cache_(TypeCache::Get()),
operation_typer_(isolate, zone()) {
......@@ -222,10 +218,6 @@ class Typer::Visitor : public Reducer {
Zone* zone() { return typer_->zone(); }
Isolate* isolate() { return typer_->isolate(); }
Graph* graph() { return typer_->graph(); }
Typer::Flags flags() const { return typer_->flags(); }
CompilationDependencies* dependencies() const {
return typer_->dependencies();
}
void SetWeakened(NodeId node_id) { weakened_nodes_.insert(node_id); }
bool IsWeakened(NodeId node_id) {
......@@ -1897,6 +1889,11 @@ Type* Typer::Visitor::TypeCheckBounds(Node* node) {
return Type::Unsigned31();
}
Type* Typer::Visitor::TypeCheckMaps(Node* node) {
UNREACHABLE();
return nullptr;
}
Type* Typer::Visitor::TypeCheckNumber(Node* node) {
Type* arg = Operand(node, 0);
return Type::Intersect(arg, Type::Number(), zone());
......@@ -1947,57 +1944,10 @@ Type* Typer::Visitor::TypeCheckTaggedHole(Node* node) {
Type* Typer::Visitor::TypeAllocate(Node* node) { return Type::TaggedPointer(); }
namespace {
MaybeHandle<Map> GetStableMapFromObjectType(Type* object_type) {
if (object_type->IsConstant() &&
object_type->AsConstant()->Value()->IsHeapObject()) {
Handle<Map> object_map(
Handle<HeapObject>::cast(object_type->AsConstant()->Value())->map());
if (object_map->is_stable()) return object_map;
} else if (object_type->IsClass()) {
Handle<Map> object_map = object_type->AsClass()->Map();
if (object_map->is_stable()) return object_map;
}
return MaybeHandle<Map>();
}
} // namespace
Type* Typer::Visitor::TypeLoadField(Node* node) {
FieldAccess const& access = FieldAccessOf(node->op());
if (access.base_is_tagged == kTaggedBase &&
access.offset == HeapObject::kMapOffset) {
// The type of LoadField[Map](o) is Constant(map) if map is stable and
// either
// (a) o has type Constant(object) and map == object->map, or
// (b) o has type Class(map),
// and either
// (1) map cannot transition further, or
// (2) deoptimization is enabled and we can add a code dependency on the
// stability of map (to guard the Constant type information).
Type* const object = Operand(node, 0);
if (object->Is(Type::None())) return Type::None();
Handle<Map> object_map;
if (GetStableMapFromObjectType(object).ToHandle(&object_map)) {
if (object_map->CanTransition()) {
if (flags() & kDeoptimizationEnabled) {
dependencies()->AssumeMapStable(object_map);
} else {
return access.type;
}
}
Type* object_map_type = Type::Constant(object_map, zone());
DCHECK(object_map_type->Is(access.type));
return object_map_type;
}
}
return access.type;
return FieldAccessOf(node->op()).type;
}
Type* Typer::Visitor::TypeLoadBuffer(Node* node) {
// TODO(bmeurer): This typing is not yet correct. Since we can still access
// out of bounds, the type in the general case has to include Undefined.
......
......@@ -5,7 +5,6 @@
#ifndef V8_COMPILER_TYPER_H_
#define V8_COMPILER_TYPER_H_
#include "src/base/flags.h"
#include "src/compiler/graph.h"
#include "src/compiler/operation-typer.h"
#include "src/types.h"
......@@ -14,7 +13,6 @@ namespace v8 {
namespace internal {
// Forward declarations.
class CompilationDependencies;
class TypeCache;
namespace compiler {
......@@ -24,15 +22,7 @@ class OperationTyper;
class Typer {
public:
// Flags that control the mode of operation.
enum Flag {
kNoFlags = 0u,
kDeoptimizationEnabled = 1u << 0,
};
typedef base::Flags<Flag> Flags;
Typer(Isolate* isolate, Graph* graph, Flags flags = kNoFlags,
CompilationDependencies* dependencies = nullptr);
Typer(Isolate* isolate, Graph* graph);
~Typer();
void Run();
......@@ -47,14 +37,10 @@ class Typer {
Graph* graph() const { return graph_; }
Zone* zone() const { return graph()->zone(); }
Isolate* isolate() const { return isolate_; }
Flags flags() const { return flags_; }
CompilationDependencies* dependencies() const { return dependencies_; }
OperationTyper* operation_typer() { return &operation_typer_; }
Isolate* const isolate_;
Graph* const graph_;
Flags const flags_;
CompilationDependencies* const dependencies_;
Decorator* decorator_;
TypeCache const& cache_;
OperationTyper operation_typer_;
......@@ -70,8 +56,6 @@ class Typer {
DISALLOW_COPY_AND_ASSIGN(Typer);
};
DEFINE_OPERATORS_FOR_FLAGS(Typer::Flags)
} // namespace compiler
} // namespace internal
} // namespace v8
......
......@@ -972,6 +972,14 @@ void Verifier::Visitor::Check(Node* node) {
CheckValueInputIs(node, 1, Type::Unsigned31());
CheckUpperIs(node, Type::Unsigned31());
break;
case IrOpcode::kCheckMaps:
// (Any, Internal, ..., Internal) -> Any
CheckValueInputIs(node, 0, Type::Any());
for (int i = 1; i < node->op()->ValueInputCount(); ++i) {
CheckValueInputIs(node, i, Type::Internal());
}
CheckNotTyped(node);
break;
case IrOpcode::kCheckNumber:
CheckValueInputIs(node, 0, Type::Any());
CheckUpperIs(node, Type::Number());
......
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