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,
......
......@@ -143,30 +143,28 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
}
// Check for the monomorphic cases.
if (access_infos.size() == 1 &&
HasOnlyStringMaps(access_infos[0].receiver_maps())) {
// Monormorphic string access (ignoring the fact that there are multiple
// String maps).
receiver = effect = graph()->NewNode(simplified()->CheckString(), receiver,
effect, control);
// Generate the actual property access.
ValueEffectControl continuation =
BuildPropertyAccess(receiver, value, effect, control, name,
native_context, access_infos[0], access_mode);
value = continuation.value();
effect = continuation.effect();
control = continuation.control();
} else if (access_infos.size() == 1 &&
HasOnlyNumberMaps(access_infos[0].receiver_maps())) {
// Monomorphic number access (we also deal with Smis here).
receiver = effect = graph()->NewNode(simplified()->CheckNumber(), receiver,
effect, control);
if (access_infos.size() == 1) {
PropertyAccessInfo access_info = access_infos.front();
if (HasOnlyStringMaps(access_info.receiver_maps())) {
// Monormorphic string access (ignoring the fact that there are multiple
// String maps).
receiver = effect = graph()->NewNode(simplified()->CheckString(),
receiver, effect, control);
} else if (HasOnlyNumberMaps(access_info.receiver_maps())) {
// Monomorphic number access (we also deal with Smis here).
receiver = effect = graph()->NewNode(simplified()->CheckNumber(),
receiver, effect, control);
} else {
// Monomorphic property access.
effect = BuildCheckTaggedPointer(receiver, effect, control);
effect = BuildCheckMaps(receiver, effect, control,
access_info.receiver_maps());
}
// Generate the actual property access.
ValueEffectControl continuation =
BuildPropertyAccess(receiver, value, effect, control, name,
native_context, access_infos[0], access_mode);
native_context, access_info, access_mode);
value = continuation.value();
effect = continuation.effect();
control = continuation.control();
......@@ -196,8 +194,7 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
receiverissmi_control = graph()->NewNode(common()->IfTrue(), branch);
receiverissmi_effect = effect;
} else {
receiver = effect = graph()->NewNode(simplified()->CheckTaggedPointer(),
receiver, effect, control);
effect = BuildCheckTaggedPointer(receiver, effect, control);
}
// Load the {receiver} map. The resulting effect is the dominating effect
......@@ -434,139 +431,179 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
node, DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess);
}
// The final states for every polymorphic branch. We join them with
// Merge+Phi+EffectPhi at the bottom.
ZoneVector<Node*> values(zone());
ZoneVector<Node*> effects(zone());
ZoneVector<Node*> controls(zone());
// Ensure that {receiver} is a heap object.
receiver = effect = graph()->NewNode(simplified()->CheckTaggedPointer(),
receiver, effect, control);
// Generate code for the various different element access patterns.
Node* fallthrough_control = control;
for (size_t j = 0; j < access_infos.size(); ++j) {
ElementAccessInfo const& access_info = access_infos[j];
Node* this_receiver = receiver;
Node* this_value = value;
Node* this_index = index;
Node* this_effect = effect;
Node* this_control = fallthrough_control;
effect = BuildCheckTaggedPointer(receiver, effect, control);
// Check for the monomorphic case.
if (access_infos.size() == 1) {
ElementAccessInfo access_info = access_infos.front();
// Perform possible elements kind transitions.
for (auto transition : access_info.transitions()) {
Handle<Map> const transition_source = transition.first;
Handle<Map> const transition_target = transition.second;
this_effect = graph()->NewNode(
effect = graph()->NewNode(
simplified()->TransitionElementsKind(
IsSimpleMapChangeTransition(transition_source->elements_kind(),
transition_target->elements_kind())
? ElementsTransition::kFastTransition
: ElementsTransition::kSlowTransition),
receiver, jsgraph()->HeapConstant(transition_source),
jsgraph()->HeapConstant(transition_target), this_effect,
this_control);
jsgraph()->HeapConstant(transition_target), effect, control);
}
// Load the {receiver} map.
Node* receiver_map = this_effect =
graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
receiver, this_effect, this_control);
// Perform map check on {receiver}.
MapList const& receiver_maps = access_info.receiver_maps();
{
ZoneVector<Node*> this_controls(zone());
ZoneVector<Node*> this_effects(zone());
size_t num_classes = receiver_maps.size();
for (Handle<Map> map : receiver_maps) {
DCHECK_LT(0u, num_classes);
Node* check =
graph()->NewNode(simplified()->ReferenceEqual(Type::Any()),
receiver_map, jsgraph()->Constant(map));
if (--num_classes == 0 && j == access_infos.size() - 1) {
// Last map check on the fallthrough control path, do a conditional
// eager deoptimization exit here.
// TODO(turbofan): This is ugly as hell! We should probably introduce
// macro-ish operators for property access that encapsulate this whole
// mess.
check = graph()->NewNode(simplified()->CheckIf(), check, this_effect,
this_control);
this_controls.push_back(this_control);
this_effects.push_back(check);
fallthrough_control = nullptr;
// TODO(turbofan): The effect/control linearization will not find a
// FrameState after the StoreField or Call that is generated for the
// elements kind transition above. This is because those operators
// don't have the kNoWrite flag on it, even though they are not
// observable by JavaScript.
effect =
graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
// Perform map check on the {receiver}.
effect =
BuildCheckMaps(receiver, effect, control, access_info.receiver_maps());
// Access the actual element.
ValueEffectControl continuation =
BuildElementAccess(receiver, index, value, effect, control,
native_context, access_info, access_mode);
value = continuation.value();
effect = continuation.effect();
control = continuation.control();
} else {
// The final states for every polymorphic branch. We join them with
// Merge+Phi+EffectPhi at the bottom.
ZoneVector<Node*> values(zone());
ZoneVector<Node*> effects(zone());
ZoneVector<Node*> controls(zone());
// Generate code for the various different element access patterns.
Node* fallthrough_control = control;
for (size_t j = 0; j < access_infos.size(); ++j) {
ElementAccessInfo const& access_info = access_infos[j];
Node* this_receiver = receiver;
Node* this_value = value;
Node* this_index = index;
Node* this_effect = effect;
Node* this_control = fallthrough_control;
// Perform possible elements kind transitions.
for (auto transition : access_info.transitions()) {
Handle<Map> const transition_source = transition.first;
Handle<Map> const transition_target = transition.second;
this_effect = graph()->NewNode(
simplified()->TransitionElementsKind(
IsSimpleMapChangeTransition(transition_source->elements_kind(),
transition_target->elements_kind())
? ElementsTransition::kFastTransition
: ElementsTransition::kSlowTransition),
receiver, jsgraph()->HeapConstant(transition_source),
jsgraph()->HeapConstant(transition_target), this_effect,
this_control);
}
// Load the {receiver} map.
Node* receiver_map = this_effect =
graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
receiver, this_effect, this_control);
// Perform map check(s) on {receiver}.
MapList const& receiver_maps = access_info.receiver_maps();
{
ZoneVector<Node*> this_controls(zone());
ZoneVector<Node*> this_effects(zone());
size_t num_classes = receiver_maps.size();
for (Handle<Map> map : receiver_maps) {
DCHECK_LT(0u, num_classes);
Node* check =
graph()->NewNode(simplified()->ReferenceEqual(Type::Any()),
receiver_map, jsgraph()->Constant(map));
if (--num_classes == 0 && j == access_infos.size() - 1) {
// Last map check on the fallthrough control path, do a conditional
// eager deoptimization exit here.
// TODO(turbofan): This is ugly as hell! We should probably
// introduce macro-ish operators for property access that
// encapsulate this whole mess.
check = graph()->NewNode(simplified()->CheckIf(), check,
this_effect, this_control);
this_controls.push_back(this_control);
this_effects.push_back(check);
fallthrough_control = nullptr;
} else {
Node* branch = graph()->NewNode(common()->Branch(), check,
fallthrough_control);
this_controls.push_back(
graph()->NewNode(common()->IfTrue(), branch));
this_effects.push_back(effect);
fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
}
}
// 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 {
Node* branch =
graph()->NewNode(common()->Branch(), check, fallthrough_control);
this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch));
this_effects.push_back(effect);
fallthrough_control = graph()->NewNode(common()->IfFalse(), branch);
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());
// TODO(turbofan): The effect/control linearization will not find a
// FrameState after the StoreField or Call that is generated for the
// elements kind transition above. This is because those operators
// don't have the kNoWrite flag on it, even though they are not
// observable by JavaScript.
this_effect = graph()->NewNode(common()->Checkpoint(), frame_state,
this_effect, this_control);
}
}
// 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());
// TODO(turbofan): The effect/control linearization will not find a
// FrameState after the StoreField or Call that is generated for the
// elements kind transition above. This is because those operators
// don't have the kNoWrite flag on it, even though they are not
// observable by JavaScript.
this_effect = graph()->NewNode(common()->Checkpoint(), frame_state,
this_effect, this_control);
// Certain stores need a prototype chain check because shape changes
// could allow callbacks on elements in the prototype chain that are
// not compatible with (monomorphic) keyed stores.
Handle<JSObject> holder;
if (access_info.holder().ToHandle(&holder)) {
AssumePrototypesStable(receiver_maps, native_context, holder);
}
}
// Certain stores need a prototype chain check because shape changes
// could allow callbacks on elements in the prototype chain that are
// not compatible with (monomorphic) keyed stores.
Handle<JSObject> holder;
if (access_info.holder().ToHandle(&holder)) {
AssumePrototypesStable(receiver_maps, native_context, holder);
// Access the actual element.
ValueEffectControl continuation = BuildElementAccess(
this_receiver, this_index, this_value, this_effect, this_control,
native_context, access_info, access_mode);
values.push_back(continuation.value());
effects.push_back(continuation.effect());
controls.push_back(continuation.control());
}
// Access the actual element.
ValueEffectControl continuation = BuildElementAccess(
this_receiver, this_index, this_value, this_effect, this_control,
native_context, access_info, access_mode);
values.push_back(continuation.value());
effects.push_back(continuation.effect());
controls.push_back(continuation.control());
}
DCHECK_NULL(fallthrough_control);
DCHECK_NULL(fallthrough_control);
// Generate the final merge point for all (polymorphic) branches.
int const control_count = static_cast<int>(controls.size());
if (control_count == 0) {
value = effect = control = jsgraph()->Dead();
} else if (control_count == 1) {
value = values.front();
effect = effects.front();
control = controls.front();
} else {
control = graph()->NewNode(common()->Merge(control_count), control_count,
&controls.front());
values.push_back(control);
value = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, control_count),
control_count + 1, &values.front());
effects.push_back(control);
effect = graph()->NewNode(common()->EffectPhi(control_count),
control_count + 1, &effects.front());
// Generate the final merge point for all (polymorphic) branches.
int const control_count = static_cast<int>(controls.size());
if (control_count == 0) {
value = effect = control = jsgraph()->Dead();
} else if (control_count == 1) {
value = values.front();
effect = effects.front();
control = controls.front();
} else {
control = graph()->NewNode(common()->Merge(control_count), control_count,
&controls.front());
values.push_back(control);
value = graph()->NewNode(
common()->Phi(MachineRepresentation::kTagged, control_count),
control_count + 1, &values.front());
effects.push_back(control);
effect = graph()->NewNode(common()->EffectPhi(control_count),
control_count + 1, &effects.front());
}
}
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
......@@ -792,18 +829,15 @@ JSNativeContextSpecialization::BuildPropertyAccess(
value = effect = graph()->NewNode(simplified()->CheckTaggedSigned(),
value, effect, control);
} else if (field_type->Is(Type::TaggedPointer())) {
// Ensure that {value} is a HeapObject.
value = effect = graph()->NewNode(simplified()->CheckTaggedPointer(),
value, effect, control);
if (field_type->NumClasses() == 1) {
// Emit a map check for the value.
Node* value_map = effect =
graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
value, effect, control);
Node* check = graph()->NewNode(
simplified()->ReferenceEqual(Type::Internal()), value_map,
jsgraph()->Constant(field_type->Classes().Current()));
effect =
graph()->NewNode(simplified()->CheckIf(), check, effect, control);
Node* field_map =
jsgraph()->Constant(field_type->Classes().Current());
effect = graph()->NewNode(simplified()->CheckMaps(1), value,
field_map, effect, control);
} else {
DCHECK_EQ(0, field_type->NumClasses());
}
......@@ -967,6 +1001,61 @@ JSNativeContextSpecialization::BuildElementAccess(
return ValueEffectControl(value, effect, control);
}
Node* JSNativeContextSpecialization::BuildCheckMaps(
Node* receiver, Node* effect, Node* control,
std::vector<Handle<Map>> const& maps) {
HeapObjectMatcher m(receiver);
if (m.HasValue()) {
Handle<Map> receiver_map(m.Value()->map(), isolate());
if (receiver_map->is_stable()) {
for (Handle<Map> map : maps) {
if (map.is_identical_to(receiver_map)) {
dependencies()->AssumeMapStable(receiver_map);
return effect;
}
}
}
}
int const map_input_count = static_cast<int>(maps.size());
int const input_count = 1 + map_input_count + 1 + 1;
Node** inputs = zone()->NewArray<Node*>(input_count);
inputs[0] = receiver;
for (int i = 0; i < map_input_count; ++i) {
inputs[1 + i] = jsgraph()->HeapConstant(maps[i]);
}
inputs[input_count - 2] = effect;
inputs[input_count - 1] = control;
return graph()->NewNode(simplified()->CheckMaps(map_input_count), input_count,
inputs);
}
Node* JSNativeContextSpecialization::BuildCheckTaggedPointer(Node* receiver,
Node* effect,
Node* control) {
switch (receiver->opcode()) {
case IrOpcode::kHeapConstant:
case IrOpcode::kJSCreate:
case IrOpcode::kJSCreateArguments:
case IrOpcode::kJSCreateArray:
case IrOpcode::kJSCreateClosure:
case IrOpcode::kJSCreateIterResultObject:
case IrOpcode::kJSCreateLiteralArray:
case IrOpcode::kJSCreateLiteralObject:
case IrOpcode::kJSCreateLiteralRegExp:
case IrOpcode::kJSConvertReceiver:
case IrOpcode::kJSToName:
case IrOpcode::kJSToString:
case IrOpcode::kJSToObject:
case IrOpcode::kJSTypeOf: {
return effect;
}
default: {
return graph()->NewNode(simplified()->CheckTaggedPointer(), receiver,
effect, control);
}
}
}
void JSNativeContextSpecialization::AssumePrototypesStable(
std::vector<Handle<Map>> const& receiver_maps,
Handle<Context> native_context, Handle<JSObject> holder) {
......@@ -1011,8 +1100,11 @@ bool JSNativeContextSpecialization::ExtractReceiverMaps(
MaybeHandle<Map> JSNativeContextSpecialization::InferReceiverMap(Node* receiver,
Node* effect) {
NodeMatcher m(receiver);
if (m.IsJSCreate()) {
HeapObjectMatcher m(receiver);
if (m.HasValue()) {
Handle<Map> receiver_map(m.Value()->map(), isolate());
if (receiver_map->is_stable()) return receiver_map;
} else if (m.IsJSCreate()) {
HeapObjectMatcher mtarget(m.InputAt(0));
HeapObjectMatcher mnewtarget(m.InputAt(1));
if (mtarget.HasValue() && mnewtarget.HasValue()) {
......@@ -1036,6 +1128,7 @@ MaybeHandle<Map> JSNativeContextSpecialization::InferReceiverMap(Node* receiver,
}
}
}
// TODO(turbofan): Go hunting for CheckMaps(receiver) in the effect chain?
return MaybeHandle<Map>();
}
......
......@@ -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