[turbofan] Track multiple maps for LoadElimination.
Store maps on the CheckMaps operator instead of burning inputs for the individual maps. Use the same data structure (the ZoneHandleSet) in the LoadElimination to track multiple maps per object. BUG=v8:5267 R=jarin@chromium.org Review-Url: https://codereview.chromium.org/2431563002 Cr-Commit-Position: refs/heads/master@{#42010}
This commit is contained in:
parent
09cb6efd58
commit
45aa13514b
1
BUILD.gn
1
BUILD.gn
@ -1783,6 +1783,7 @@ v8_source_set("v8_base") {
|
||||
"src/zone/zone-allocator.h",
|
||||
"src/zone/zone-chunk-list.h",
|
||||
"src/zone/zone-containers.h",
|
||||
"src/zone/zone-handle-set.h",
|
||||
"src/zone/zone-segment.cc",
|
||||
"src/zone/zone-segment.h",
|
||||
"src/zone/zone.cc",
|
||||
|
@ -1202,18 +1202,20 @@ EffectControlLinearizer::LowerCheckBounds(Node* node, Node* frame_state,
|
||||
EffectControlLinearizer::ValueEffectControl
|
||||
EffectControlLinearizer::LowerCheckMaps(Node* node, Node* frame_state,
|
||||
Node* effect, Node* control) {
|
||||
CheckMapsParameters const& p = CheckMapsParametersOf(node->op());
|
||||
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;
|
||||
ZoneHandleSet<Map> const& maps = p.maps();
|
||||
int const map_count = static_cast<int>(maps.size());
|
||||
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* map = jsgraph()->HeapConstant(maps[i]);
|
||||
|
||||
Node* check = graph()->NewNode(machine()->WordEqual(), value_map, map);
|
||||
if (i == map_count - 1) {
|
||||
@ -3002,8 +3004,8 @@ EffectControlLinearizer::LowerTransitionElementsKind(Node* node, Node* effect,
|
||||
Node* control) {
|
||||
ElementsTransition const transition = ElementsTransitionOf(node->op());
|
||||
Node* object = node->InputAt(0);
|
||||
Node* source_map = node->InputAt(1);
|
||||
Node* target_map = node->InputAt(2);
|
||||
Node* source_map = jsgraph()->HeapConstant(transition.source());
|
||||
Node* target_map = jsgraph()->HeapConstant(transition.target());
|
||||
|
||||
// Load the current map of {object}.
|
||||
Node* object_map = effect =
|
||||
@ -3020,7 +3022,7 @@ EffectControlLinearizer::LowerTransitionElementsKind(Node* node, Node* effect,
|
||||
Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
|
||||
Node* etrue = effect;
|
||||
{
|
||||
switch (transition) {
|
||||
switch (transition.mode()) {
|
||||
case ElementsTransition::kFastTransition: {
|
||||
// In-place migration of {object}, just store the {target_map}.
|
||||
etrue =
|
||||
|
@ -128,11 +128,10 @@ MaybeHandle<Map> GetMapWitness(Node* node) {
|
||||
for (Node* dominator = effect;;) {
|
||||
if (dominator->opcode() == IrOpcode::kCheckMaps &&
|
||||
IsSame(dominator->InputAt(0), receiver)) {
|
||||
if (dominator->op()->ValueInputCount() == 2) {
|
||||
HeapObjectMatcher m(dominator->InputAt(1));
|
||||
if (m.HasValue()) return Handle<Map>::cast(m.Value());
|
||||
}
|
||||
return MaybeHandle<Map>();
|
||||
ZoneHandleSet<Map> const& maps =
|
||||
CheckMapsParametersOf(dominator->op()).maps();
|
||||
return (maps.size() == 1) ? MaybeHandle<Map>(maps[0])
|
||||
: MaybeHandle<Map>();
|
||||
}
|
||||
if (dominator->op()->EffectInputCount() != 1) {
|
||||
// Didn't find any appropriate CheckMaps node.
|
||||
@ -413,12 +412,17 @@ Reduction JSBuiltinReducer::ReduceFastArrayIteratorNext(
|
||||
} else {
|
||||
// For value/entry iteration, first step is a mapcheck to ensure
|
||||
// inlining is still valid.
|
||||
Node* array_map = etrue1 =
|
||||
graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
|
||||
array, etrue1, if_true1);
|
||||
Node* orig_map = etrue1 =
|
||||
graph()->NewNode(simplified()->LoadField(
|
||||
AccessBuilder::ForJSArrayIteratorObjectMap()),
|
||||
iterator, etrue1, if_true1);
|
||||
etrue1 = graph()->NewNode(simplified()->CheckMaps(1), array, orig_map,
|
||||
etrue1, if_true1);
|
||||
Node* check_map = graph()->NewNode(simplified()->ReferenceEqual(),
|
||||
array_map, orig_map);
|
||||
etrue1 = graph()->NewNode(simplified()->CheckIf(), check_map, etrue1,
|
||||
if_true1);
|
||||
}
|
||||
|
||||
if (kind != IterationKind::kKeys) {
|
||||
@ -910,14 +914,11 @@ bool HasInstanceTypeWitness(Node* receiver, Node* effect,
|
||||
for (Node* dominator = effect;;) {
|
||||
if (dominator->opcode() == IrOpcode::kCheckMaps &&
|
||||
IsSame(dominator->InputAt(0), receiver)) {
|
||||
ZoneHandleSet<Map> const& maps =
|
||||
CheckMapsParametersOf(dominator->op()).maps();
|
||||
// Check if all maps have the given {instance_type}.
|
||||
for (int i = 1; i < dominator->op()->ValueInputCount(); ++i) {
|
||||
Node* const map = NodeProperties::GetValueInput(dominator, i);
|
||||
Type* const map_type = NodeProperties::GetType(map);
|
||||
if (!map_type->IsHeapConstant()) return false;
|
||||
Handle<Map> const map_value =
|
||||
Handle<Map>::cast(map_type->AsHeapConstant()->Value());
|
||||
if (map_value->instance_type() != instance_type) return false;
|
||||
for (size_t i = 0; i < maps.size(); ++i) {
|
||||
if (maps[i]->instance_type() != instance_type) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -206,9 +206,9 @@ Reduction JSGlobalObjectSpecialization::ReduceJSStoreGlobal(Node* node) {
|
||||
value, effect, control);
|
||||
|
||||
// Check {value} map agains the {property_cell} map.
|
||||
effect = graph()->NewNode(
|
||||
simplified()->CheckMaps(1), value,
|
||||
jsgraph()->HeapConstant(property_cell_value_map), effect, control);
|
||||
effect = graph()->NewNode(simplified()->CheckMaps(ZoneHandleSet<Map>(
|
||||
property_cell_value_map)),
|
||||
value, effect, control);
|
||||
property_cell_value_type = Type::OtherInternal();
|
||||
representation = MachineRepresentation::kTaggedPointer;
|
||||
} else {
|
||||
|
@ -307,11 +307,10 @@ bool NeedsConvertReceiver(Node* receiver, Node* effect) {
|
||||
if (dominator->opcode() == IrOpcode::kCheckMaps &&
|
||||
IsSame(dominator->InputAt(0), receiver)) {
|
||||
// Check if all maps have the given {instance_type}.
|
||||
for (int i = 1; i < dominator->op()->ValueInputCount(); ++i) {
|
||||
HeapObjectMatcher m(NodeProperties::GetValueInput(dominator, i));
|
||||
if (!m.HasValue()) return true;
|
||||
Handle<Map> const map = Handle<Map>::cast(m.Value());
|
||||
if (!map->IsJSReceiverMap()) return true;
|
||||
ZoneHandleSet<Map> const& maps =
|
||||
CheckMapsParametersOf(dominator->op()).maps();
|
||||
for (size_t i = 0; i < maps.size(); ++i) {
|
||||
if (!maps[i]->IsJSReceiverMap()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -643,13 +643,13 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
|
||||
Handle<Map> const transition_source = transition.first;
|
||||
Handle<Map> const transition_target = transition.second;
|
||||
effect = graph()->NewNode(
|
||||
simplified()->TransitionElementsKind(
|
||||
simplified()->TransitionElementsKind(ElementsTransition(
|
||||
IsSimpleMapChangeTransition(transition_source->elements_kind(),
|
||||
transition_target->elements_kind())
|
||||
? ElementsTransition::kFastTransition
|
||||
: ElementsTransition::kSlowTransition),
|
||||
receiver, jsgraph()->HeapConstant(transition_source),
|
||||
jsgraph()->HeapConstant(transition_target), effect, control);
|
||||
: ElementsTransition::kSlowTransition,
|
||||
transition_source, transition_target)),
|
||||
receiver, effect, control);
|
||||
}
|
||||
|
||||
// TODO(turbofan): The effect/control linearization will not find a
|
||||
@ -694,14 +694,13 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
|
||||
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);
|
||||
ElementsTransition(IsSimpleMapChangeTransition(
|
||||
transition_source->elements_kind(),
|
||||
transition_target->elements_kind())
|
||||
? ElementsTransition::kFastTransition
|
||||
: ElementsTransition::kSlowTransition,
|
||||
transition_source, transition_target)),
|
||||
receiver, this_effect, this_control);
|
||||
}
|
||||
|
||||
// Load the {receiver} map.
|
||||
@ -1179,9 +1178,9 @@ JSNativeContextSpecialization::BuildPropertyAccess(
|
||||
Handle<Map> field_map;
|
||||
if (access_info.field_map().ToHandle(&field_map)) {
|
||||
// Emit a map check for the value.
|
||||
effect = graph()->NewNode(simplified()->CheckMaps(1), value,
|
||||
jsgraph()->HeapConstant(field_map),
|
||||
effect, control);
|
||||
effect = graph()->NewNode(
|
||||
simplified()->CheckMaps(ZoneHandleSet<Map>(field_map)), value,
|
||||
effect, control);
|
||||
}
|
||||
field_access.write_barrier_kind = kPointerWriteBarrier;
|
||||
break;
|
||||
@ -1418,9 +1417,9 @@ JSNativeContextSpecialization::BuildElementAccess(
|
||||
if (access_mode == AccessMode::kStore &&
|
||||
IsFastSmiOrObjectElementsKind(elements_kind) &&
|
||||
store_mode != STORE_NO_TRANSITION_HANDLE_COW) {
|
||||
effect =
|
||||
graph()->NewNode(simplified()->CheckMaps(1), elements,
|
||||
jsgraph()->FixedArrayMapConstant(), effect, control);
|
||||
effect = graph()->NewNode(simplified()->CheckMaps(ZoneHandleSet<Map>(
|
||||
factory()->fixed_array_map())),
|
||||
elements, effect, control);
|
||||
}
|
||||
|
||||
// Check if the {receiver} is a JSArray.
|
||||
@ -1640,12 +1639,12 @@ Node* JSNativeContextSpecialization::BuildCheckHeapObject(Node* receiver,
|
||||
|
||||
Node* JSNativeContextSpecialization::BuildCheckMaps(
|
||||
Node* receiver, Node* effect, Node* control,
|
||||
std::vector<Handle<Map>> const& maps) {
|
||||
std::vector<Handle<Map>> const& receiver_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) {
|
||||
for (Handle<Map> map : receiver_maps) {
|
||||
if (map.is_identical_to(receiver_map)) {
|
||||
dependencies()->AssumeMapStable(receiver_map);
|
||||
return effect;
|
||||
@ -1653,17 +1652,12 @@ Node* JSNativeContextSpecialization::BuildCheckMaps(
|
||||
}
|
||||
}
|
||||
}
|
||||
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]);
|
||||
ZoneHandleSet<Map> maps;
|
||||
for (Handle<Map> map : receiver_maps) {
|
||||
maps.insert(map, graph()->zone());
|
||||
}
|
||||
inputs[input_count - 2] = effect;
|
||||
inputs[input_count - 1] = control;
|
||||
return graph()->NewNode(simplified()->CheckMaps(map_input_count), input_count,
|
||||
inputs);
|
||||
return graph()->NewNode(simplified()->CheckMaps(maps), receiver, effect,
|
||||
control);
|
||||
}
|
||||
|
||||
void JSNativeContextSpecialization::AssumePrototypesStable(
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include "src/compiler/js-graph.h"
|
||||
#include "src/compiler/node-properties.h"
|
||||
#include "src/compiler/simplified-operator.h"
|
||||
#include "src/factory.h"
|
||||
#include "src/objects-inl.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -320,6 +322,42 @@ void LoadElimination::AbstractField::Print() const {
|
||||
}
|
||||
}
|
||||
|
||||
bool LoadElimination::AbstractMaps::Lookup(
|
||||
Node* object, ZoneHandleSet<Map>* object_maps) const {
|
||||
for (auto pair : info_for_node_) {
|
||||
if (MustAlias(object, pair.first)) {
|
||||
*object_maps = pair.second;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
LoadElimination::AbstractMaps const* LoadElimination::AbstractMaps::Kill(
|
||||
Node* object, Zone* zone) const {
|
||||
for (auto pair : this->info_for_node_) {
|
||||
if (MayAlias(object, pair.first)) {
|
||||
AbstractMaps* that = new (zone) AbstractMaps(zone);
|
||||
for (auto pair : this->info_for_node_) {
|
||||
if (!MayAlias(object, pair.first)) that->info_for_node_.insert(pair);
|
||||
}
|
||||
return that;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
void LoadElimination::AbstractMaps::Print() const {
|
||||
for (auto pair : info_for_node_) {
|
||||
PrintF(" #%d:%s\n", pair.first->id(), pair.first->op()->mnemonic());
|
||||
OFStream os(stdout);
|
||||
ZoneHandleSet<Map> const& maps = pair.second;
|
||||
for (size_t i = 0; i < maps.size(); ++i) {
|
||||
os << " - " << Brief(*maps[i]) << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool LoadElimination::AbstractState::Equals(AbstractState const* that) const {
|
||||
if (this->checks_) {
|
||||
if (!that->checks_ || !that->checks_->Equals(this->checks_)) {
|
||||
@ -344,6 +382,13 @@ bool LoadElimination::AbstractState::Equals(AbstractState const* that) const {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (this->maps_) {
|
||||
if (!that->maps_ || !that->maps_->Equals(this->maps_)) {
|
||||
return false;
|
||||
}
|
||||
} else if (that->maps_) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -372,6 +417,11 @@ void LoadElimination::AbstractState::Merge(AbstractState const* that,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Merge the information we have about the maps.
|
||||
if (this->maps_) {
|
||||
this->maps_ = that->maps_ ? that->maps_->Merge(this->maps_, zone) : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Node* LoadElimination::AbstractState::LookupCheck(Node* node) const {
|
||||
@ -389,6 +439,35 @@ LoadElimination::AbstractState const* LoadElimination::AbstractState::AddCheck(
|
||||
return that;
|
||||
}
|
||||
|
||||
bool LoadElimination::AbstractState::LookupMaps(
|
||||
Node* object, ZoneHandleSet<Map>* object_map) const {
|
||||
return this->maps_ && this->maps_->Lookup(object, object_map);
|
||||
}
|
||||
|
||||
LoadElimination::AbstractState const* LoadElimination::AbstractState::AddMaps(
|
||||
Node* object, ZoneHandleSet<Map> maps, Zone* zone) const {
|
||||
AbstractState* that = new (zone) AbstractState(*this);
|
||||
if (that->maps_) {
|
||||
that->maps_ = that->maps_->Extend(object, maps, zone);
|
||||
} else {
|
||||
that->maps_ = new (zone) AbstractMaps(object, maps, zone);
|
||||
}
|
||||
return that;
|
||||
}
|
||||
|
||||
LoadElimination::AbstractState const* LoadElimination::AbstractState::KillMaps(
|
||||
Node* object, Zone* zone) const {
|
||||
if (this->maps_) {
|
||||
AbstractMaps const* that_maps = this->maps_->Kill(object, zone);
|
||||
if (this->maps_ != that_maps) {
|
||||
AbstractState* that = new (zone) AbstractState(*this);
|
||||
that->maps_ = that_maps;
|
||||
return that;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
Node* LoadElimination::AbstractState::LookupElement(Node* object,
|
||||
Node* index) const {
|
||||
if (this->elements_) {
|
||||
@ -481,6 +560,10 @@ void LoadElimination::AbstractState::Print() const {
|
||||
PrintF(" checks:\n");
|
||||
checks_->Print();
|
||||
}
|
||||
if (maps_) {
|
||||
PrintF(" maps:\n");
|
||||
maps_->Print();
|
||||
}
|
||||
if (elements_) {
|
||||
PrintF(" elements:\n");
|
||||
elements_->Print();
|
||||
@ -520,23 +603,18 @@ Reduction LoadElimination::ReduceArrayBufferWasNeutered(Node* node) {
|
||||
}
|
||||
|
||||
Reduction LoadElimination::ReduceCheckMaps(Node* node) {
|
||||
ZoneHandleSet<Map> const maps = CheckMapsParametersOf(node->op()).maps();
|
||||
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, FieldIndexOf(HeapObject::kMapOffset))) {
|
||||
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, FieldIndexOf(HeapObject::kMapOffset), map0,
|
||||
zone());
|
||||
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);
|
||||
}
|
||||
|
||||
@ -546,18 +624,16 @@ Reduction LoadElimination::ReduceEnsureWritableFastElements(Node* node) {
|
||||
Node* const effect = NodeProperties::GetEffectInput(node);
|
||||
AbstractState const* state = node_states_.Get(effect);
|
||||
if (state == nullptr) return NoChange();
|
||||
Node* fixed_array_map = jsgraph()->FixedArrayMapConstant();
|
||||
if (Node* const elements_map =
|
||||
state->LookupField(elements, FieldIndexOf(HeapObject::kMapOffset))) {
|
||||
// Check if the {elements} already have the fixed array map.
|
||||
if (elements_map == fixed_array_map) {
|
||||
ReplaceWithValue(node, elements, effect);
|
||||
return Replace(elements);
|
||||
}
|
||||
ZoneHandleSet<Map> elements_maps;
|
||||
ZoneHandleSet<Map> fixed_array_maps(factory()->fixed_array_map());
|
||||
if (state->LookupMaps(elements, &elements_maps) &&
|
||||
fixed_array_maps.contains(elements_maps)) {
|
||||
ReplaceWithValue(node, elements, effect);
|
||||
return Replace(elements);
|
||||
}
|
||||
// We know that the resulting elements have the fixed array map.
|
||||
state = state->AddField(node, FieldIndexOf(HeapObject::kMapOffset),
|
||||
fixed_array_map, zone());
|
||||
state = state->AddMaps(node, fixed_array_maps, zone());
|
||||
// Kill the previous elements on {object}.
|
||||
state =
|
||||
state->KillField(object, FieldIndexOf(JSObject::kElementsOffset), zone());
|
||||
@ -575,14 +651,12 @@ Reduction LoadElimination::ReduceMaybeGrowFastElements(Node* node) {
|
||||
if (state == nullptr) return NoChange();
|
||||
if (flags & GrowFastElementsFlag::kDoubleElements) {
|
||||
// We know that the resulting elements have the fixed double array map.
|
||||
Node* fixed_double_array_map = jsgraph()->FixedDoubleArrayMapConstant();
|
||||
state = state->AddField(node, FieldIndexOf(HeapObject::kMapOffset),
|
||||
fixed_double_array_map, zone());
|
||||
state = state->AddMaps(
|
||||
node, ZoneHandleSet<Map>(factory()->fixed_double_array_map()), zone());
|
||||
} else {
|
||||
// We know that the resulting elements have the fixed array map.
|
||||
Node* fixed_array_map = jsgraph()->FixedArrayMapConstant();
|
||||
state = state->AddField(node, FieldIndexOf(HeapObject::kMapOffset),
|
||||
fixed_array_map, zone());
|
||||
state = state->AddMaps(
|
||||
node, ZoneHandleSet<Map>(factory()->fixed_array_map()), zone());
|
||||
}
|
||||
if (flags & GrowFastElementsFlag::kArrayObject) {
|
||||
// Kill the previous Array::length on {object}.
|
||||
@ -599,31 +673,30 @@ Reduction LoadElimination::ReduceMaybeGrowFastElements(Node* node) {
|
||||
}
|
||||
|
||||
Reduction LoadElimination::ReduceTransitionElementsKind(Node* node) {
|
||||
ElementsTransition transition = ElementsTransitionOf(node->op());
|
||||
Node* const object = NodeProperties::GetValueInput(node, 0);
|
||||
Node* const source_map = NodeProperties::GetValueInput(node, 1);
|
||||
Node* const target_map = NodeProperties::GetValueInput(node, 2);
|
||||
Handle<Map> source_map(transition.source());
|
||||
Handle<Map> target_map(transition.target());
|
||||
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, FieldIndexOf(HeapObject::kMapOffset))) {
|
||||
if (target_map == object_map) {
|
||||
ZoneHandleSet<Map> object_maps;
|
||||
if (state->LookupMaps(object, &object_maps)) {
|
||||
if (ZoneHandleSet<Map>(target_map).contains(object_maps)) {
|
||||
// The {object} already has the {target_map}, so this TransitionElements
|
||||
// {node} is fully redundant (independent of what {source_map} is).
|
||||
return Replace(effect);
|
||||
}
|
||||
state =
|
||||
state->KillField(object, FieldIndexOf(HeapObject::kMapOffset), zone());
|
||||
if (source_map == object_map) {
|
||||
state = state->AddField(object, FieldIndexOf(HeapObject::kMapOffset),
|
||||
target_map, zone());
|
||||
if (object_maps.contains(ZoneHandleSet<Map>(source_map))) {
|
||||
object_maps.remove(source_map, zone());
|
||||
object_maps.insert(target_map, zone());
|
||||
state = state->KillMaps(object, zone());
|
||||
state = state->AddMaps(object, object_maps, zone());
|
||||
}
|
||||
} else {
|
||||
state =
|
||||
state->KillField(object, FieldIndexOf(HeapObject::kMapOffset), zone());
|
||||
state = state->KillMaps(object, zone());
|
||||
}
|
||||
ElementsTransition transition = ElementsTransitionOf(node->op());
|
||||
switch (transition) {
|
||||
switch (transition.mode()) {
|
||||
case ElementsTransition::kFastTransition:
|
||||
break;
|
||||
case ElementsTransition::kSlowTransition:
|
||||
@ -642,23 +715,34 @@ Reduction LoadElimination::ReduceLoadField(Node* node) {
|
||||
Node* const control = NodeProperties::GetControlInput(node);
|
||||
AbstractState const* state = node_states_.Get(effect);
|
||||
if (state == nullptr) return NoChange();
|
||||
int field_index = FieldIndexOf(access);
|
||||
if (field_index >= 0) {
|
||||
if (Node* replacement = state->LookupField(object, field_index)) {
|
||||
// Make sure we don't resurrect dead {replacement} nodes.
|
||||
if (!replacement->IsDead()) {
|
||||
// We might need to guard the {replacement} if the type of the
|
||||
// {node} is more precise than the type of the {replacement}.
|
||||
Type* const node_type = NodeProperties::GetType(node);
|
||||
if (!NodeProperties::GetType(replacement)->Is(node_type)) {
|
||||
replacement = graph()->NewNode(common()->TypeGuard(node_type),
|
||||
replacement, control);
|
||||
}
|
||||
ReplaceWithValue(node, replacement, effect);
|
||||
return Replace(replacement);
|
||||
}
|
||||
if (access.offset == HeapObject::kMapOffset &&
|
||||
access.base_is_tagged == kTaggedBase) {
|
||||
DCHECK(IsAnyTagged(access.machine_type.representation()));
|
||||
ZoneHandleSet<Map> object_maps;
|
||||
if (state->LookupMaps(object, &object_maps) && object_maps.size() == 1) {
|
||||
Node* value = jsgraph()->HeapConstant(object_maps[0]);
|
||||
ReplaceWithValue(node, value, effect);
|
||||
return Replace(value);
|
||||
}
|
||||
} else {
|
||||
int field_index = FieldIndexOf(access);
|
||||
if (field_index >= 0) {
|
||||
if (Node* replacement = state->LookupField(object, field_index)) {
|
||||
// Make sure we don't resurrect dead {replacement} nodes.
|
||||
if (!replacement->IsDead()) {
|
||||
// We might need to guard the {replacement} if the type of the
|
||||
// {node} is more precise than the type of the {replacement}.
|
||||
Type* const node_type = NodeProperties::GetType(node);
|
||||
if (!NodeProperties::GetType(replacement)->Is(node_type)) {
|
||||
replacement = graph()->NewNode(common()->TypeGuard(node_type),
|
||||
replacement, control);
|
||||
}
|
||||
ReplaceWithValue(node, replacement, effect);
|
||||
return Replace(replacement);
|
||||
}
|
||||
}
|
||||
state = state->AddField(object, field_index, node, zone());
|
||||
}
|
||||
state = state->AddField(object, field_index, node, zone());
|
||||
}
|
||||
return UpdateState(node, state);
|
||||
}
|
||||
@ -670,19 +754,33 @@ Reduction LoadElimination::ReduceStoreField(Node* node) {
|
||||
Node* const effect = NodeProperties::GetEffectInput(node);
|
||||
AbstractState const* state = node_states_.Get(effect);
|
||||
if (state == nullptr) return NoChange();
|
||||
int field_index = FieldIndexOf(access);
|
||||
if (field_index >= 0) {
|
||||
Node* const old_value = state->LookupField(object, field_index);
|
||||
if (old_value == new_value) {
|
||||
// This store is fully redundant.
|
||||
return Replace(effect);
|
||||
if (access.offset == HeapObject::kMapOffset &&
|
||||
access.base_is_tagged == kTaggedBase) {
|
||||
DCHECK(IsAnyTagged(access.machine_type.representation()));
|
||||
// Kill all potential knowledge about the {object}s map.
|
||||
state = state->KillMaps(object, zone());
|
||||
Type* const new_value_type = NodeProperties::GetType(new_value);
|
||||
if (new_value_type->IsHeapConstant()) {
|
||||
// Record the new {object} map information.
|
||||
ZoneHandleSet<Map> object_maps(
|
||||
Handle<Map>::cast(new_value_type->AsHeapConstant()->Value()));
|
||||
state = state->AddMaps(object, object_maps, zone());
|
||||
}
|
||||
// Kill all potentially aliasing fields and record the new value.
|
||||
state = state->KillField(object, field_index, zone());
|
||||
state = state->AddField(object, field_index, new_value, zone());
|
||||
} else {
|
||||
// Unsupported StoreField operator.
|
||||
state = state->KillFields(object, zone());
|
||||
int field_index = FieldIndexOf(access);
|
||||
if (field_index >= 0) {
|
||||
Node* const old_value = state->LookupField(object, field_index);
|
||||
if (old_value == new_value) {
|
||||
// This store is fully redundant.
|
||||
return Replace(effect);
|
||||
}
|
||||
// Kill all potentially aliasing fields and record the new value.
|
||||
state = state->KillField(object, field_index, zone());
|
||||
state = state->AddField(object, field_index, new_value, zone());
|
||||
} else {
|
||||
// Unsupported StoreField operator.
|
||||
state = state->KillFields(object, zone());
|
||||
}
|
||||
}
|
||||
return UpdateState(node, state);
|
||||
}
|
||||
@ -865,13 +963,13 @@ LoadElimination::AbstractState const* LoadElimination::ComputeLoopState(
|
||||
break;
|
||||
}
|
||||
case IrOpcode::kTransitionElementsKind: {
|
||||
ElementsTransition transition = ElementsTransitionOf(current->op());
|
||||
Node* const object = NodeProperties::GetValueInput(current, 0);
|
||||
Node* const target_map = NodeProperties::GetValueInput(current, 2);
|
||||
Node* const object_map = state->LookupField(
|
||||
object, FieldIndexOf(HeapObject::kMapOffset));
|
||||
if (target_map != object_map) {
|
||||
state = state->KillField(
|
||||
object, FieldIndexOf(HeapObject::kMapOffset), zone());
|
||||
ZoneHandleSet<Map> object_maps;
|
||||
if (!state->LookupMaps(object, &object_maps) ||
|
||||
!ZoneHandleSet<Map>(transition.target())
|
||||
.contains(object_maps)) {
|
||||
state = state->KillMaps(object, zone());
|
||||
state = state->KillField(
|
||||
object, FieldIndexOf(JSObject::kElementsOffset), zone());
|
||||
}
|
||||
@ -880,11 +978,16 @@ LoadElimination::AbstractState const* LoadElimination::ComputeLoopState(
|
||||
case IrOpcode::kStoreField: {
|
||||
FieldAccess const& access = FieldAccessOf(current->op());
|
||||
Node* const object = NodeProperties::GetValueInput(current, 0);
|
||||
int field_index = FieldIndexOf(access);
|
||||
if (field_index < 0) {
|
||||
state = state->KillFields(object, zone());
|
||||
if (access.offset == HeapObject::kMapOffset) {
|
||||
// Invalidate what we know about the {object}s map.
|
||||
state = state->KillMaps(object, zone());
|
||||
} else {
|
||||
state = state->KillField(object, field_index, zone());
|
||||
int field_index = FieldIndexOf(access);
|
||||
if (field_index < 0) {
|
||||
state = state->KillFields(object, zone());
|
||||
} else {
|
||||
state = state->KillField(object, field_index, zone());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -916,7 +1019,8 @@ int LoadElimination::FieldIndexOf(int offset) {
|
||||
DCHECK_EQ(0, offset % kPointerSize);
|
||||
int field_index = offset / kPointerSize;
|
||||
if (field_index >= static_cast<int>(kMaxTrackedFields)) return -1;
|
||||
return field_index;
|
||||
DCHECK_LT(0, field_index);
|
||||
return field_index - 1;
|
||||
}
|
||||
|
||||
// static
|
||||
@ -962,6 +1066,8 @@ CommonOperatorBuilder* LoadElimination::common() const {
|
||||
|
||||
Graph* LoadElimination::graph() const { return jsgraph()->graph(); }
|
||||
|
||||
Factory* LoadElimination::factory() const { return jsgraph()->factory(); }
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -8,9 +8,14 @@
|
||||
#include "src/base/compiler-specific.h"
|
||||
#include "src/compiler/graph-reducer.h"
|
||||
#include "src/globals.h"
|
||||
#include "src/zone/zone-handle-set.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
// Forward declarations.
|
||||
class Factory;
|
||||
|
||||
namespace compiler {
|
||||
|
||||
// Foward declarations.
|
||||
@ -152,6 +157,49 @@ class V8_EXPORT_PRIVATE LoadElimination final
|
||||
|
||||
static size_t const kMaxTrackedFields = 32;
|
||||
|
||||
// Abstract state to approximate the current map of an object along the
|
||||
// effect paths through the graph.
|
||||
class AbstractMaps final : public ZoneObject {
|
||||
public:
|
||||
explicit AbstractMaps(Zone* zone) : info_for_node_(zone) {}
|
||||
AbstractMaps(Node* object, ZoneHandleSet<Map> maps, Zone* zone)
|
||||
: info_for_node_(zone) {
|
||||
info_for_node_.insert(std::make_pair(object, maps));
|
||||
}
|
||||
|
||||
AbstractMaps const* Extend(Node* object, ZoneHandleSet<Map> maps,
|
||||
Zone* zone) const {
|
||||
AbstractMaps* that = new (zone) AbstractMaps(zone);
|
||||
that->info_for_node_ = this->info_for_node_;
|
||||
that->info_for_node_.insert(std::make_pair(object, maps));
|
||||
return that;
|
||||
}
|
||||
bool Lookup(Node* object, ZoneHandleSet<Map>* object_maps) const;
|
||||
AbstractMaps const* Kill(Node* object, Zone* zone) const;
|
||||
bool Equals(AbstractMaps const* that) const {
|
||||
return this == that || this->info_for_node_ == that->info_for_node_;
|
||||
}
|
||||
AbstractMaps const* Merge(AbstractMaps const* that, Zone* zone) const {
|
||||
if (this->Equals(that)) return this;
|
||||
AbstractMaps* copy = new (zone) AbstractMaps(zone);
|
||||
for (auto this_it : this->info_for_node_) {
|
||||
Node* this_object = this_it.first;
|
||||
ZoneHandleSet<Map> this_maps = this_it.second;
|
||||
auto that_it = that->info_for_node_.find(this_object);
|
||||
if (that_it != that->info_for_node_.end() &&
|
||||
that_it->second == this_maps) {
|
||||
copy->info_for_node_.insert(this_it);
|
||||
}
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
void Print() const;
|
||||
|
||||
private:
|
||||
ZoneMap<Node*, ZoneHandleSet<Map>> info_for_node_;
|
||||
};
|
||||
|
||||
class AbstractState final : public ZoneObject {
|
||||
public:
|
||||
AbstractState() {
|
||||
@ -163,6 +211,11 @@ class V8_EXPORT_PRIVATE LoadElimination final
|
||||
bool Equals(AbstractState const* that) const;
|
||||
void Merge(AbstractState const* that, Zone* zone);
|
||||
|
||||
AbstractState const* AddMaps(Node* object, ZoneHandleSet<Map> maps,
|
||||
Zone* zone) const;
|
||||
AbstractState const* KillMaps(Node* object, Zone* zone) const;
|
||||
bool LookupMaps(Node* object, ZoneHandleSet<Map>* object_maps) const;
|
||||
|
||||
AbstractState const* AddField(Node* object, size_t index, Node* value,
|
||||
Zone* zone) const;
|
||||
AbstractState const* KillField(Node* object, size_t index,
|
||||
@ -185,6 +238,7 @@ class V8_EXPORT_PRIVATE LoadElimination final
|
||||
AbstractChecks const* checks_ = nullptr;
|
||||
AbstractElements const* elements_ = nullptr;
|
||||
AbstractField const* fields_[kMaxTrackedFields];
|
||||
AbstractMaps const* maps_ = nullptr;
|
||||
};
|
||||
|
||||
class AbstractStateForEffectNodes final : public ZoneObject {
|
||||
@ -223,6 +277,7 @@ class V8_EXPORT_PRIVATE LoadElimination final
|
||||
|
||||
CommonOperatorBuilder* common() const;
|
||||
AbstractState const* empty_state() const { return &empty_state_; }
|
||||
Factory* factory() const;
|
||||
Graph* graph() const;
|
||||
JSGraph* jsgraph() const { return jsgraph_; }
|
||||
Zone* zone() const { return node_states_.zone(); }
|
||||
|
@ -229,6 +229,32 @@ std::ostream& operator<<(std::ostream& os, CheckForMinusZeroMode mode) {
|
||||
return os;
|
||||
}
|
||||
|
||||
bool operator==(CheckMapsParameters const& lhs,
|
||||
CheckMapsParameters const& rhs) {
|
||||
return lhs.maps() == rhs.maps();
|
||||
}
|
||||
|
||||
bool operator!=(CheckMapsParameters const& lhs,
|
||||
CheckMapsParameters const& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
size_t hash_value(CheckMapsParameters const& p) { return hash_value(p.maps()); }
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, CheckMapsParameters const& p) {
|
||||
ZoneHandleSet<Map> const& maps = p.maps();
|
||||
for (size_t i = 0; i < maps.size(); ++i) {
|
||||
if (i != 0) os << ", ";
|
||||
os << Brief(*maps[i]);
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
CheckMapsParameters const& CheckMapsParametersOf(Operator const* op) {
|
||||
DCHECK_EQ(IrOpcode::kCheckMaps, op->opcode());
|
||||
return OpParameter<CheckMapsParameters>(op);
|
||||
}
|
||||
|
||||
size_t hash_value(CheckTaggedInputMode mode) {
|
||||
return static_cast<size_t>(mode);
|
||||
}
|
||||
@ -274,22 +300,36 @@ GrowFastElementsFlags GrowFastElementsFlagsOf(const Operator* op) {
|
||||
return OpParameter<GrowFastElementsFlags>(op);
|
||||
}
|
||||
|
||||
bool operator==(ElementsTransition const& lhs, ElementsTransition const& rhs) {
|
||||
return lhs.mode() == rhs.mode() &&
|
||||
lhs.source().address() == rhs.source().address() &&
|
||||
lhs.target().address() == rhs.target().address();
|
||||
}
|
||||
|
||||
bool operator!=(ElementsTransition const& lhs, ElementsTransition const& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
size_t hash_value(ElementsTransition transition) {
|
||||
return static_cast<uint8_t>(transition);
|
||||
return base::hash_combine(static_cast<uint8_t>(transition.mode()),
|
||||
transition.source().address(),
|
||||
transition.target().address());
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, ElementsTransition transition) {
|
||||
switch (transition) {
|
||||
switch (transition.mode()) {
|
||||
case ElementsTransition::kFastTransition:
|
||||
return os << "fast-transition";
|
||||
return os << "fast-transition from " << Brief(*transition.source())
|
||||
<< " to " << Brief(*transition.target());
|
||||
case ElementsTransition::kSlowTransition:
|
||||
return os << "slow-transition";
|
||||
return os << "slow-transition from " << Brief(*transition.source())
|
||||
<< " to " << Brief(*transition.target());
|
||||
}
|
||||
UNREACHABLE();
|
||||
return os;
|
||||
}
|
||||
|
||||
ElementsTransition ElementsTransitionOf(const Operator* op) {
|
||||
ElementsTransition const& ElementsTransitionOf(const Operator* op) {
|
||||
DCHECK_EQ(IrOpcode::kTransitionElementsKind, op->opcode());
|
||||
return OpParameter<ElementsTransition>(op);
|
||||
}
|
||||
@ -697,16 +737,14 @@ const Operator* SimplifiedOperatorBuilder::CheckedTaggedToFloat64(
|
||||
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::CheckMaps(ZoneHandleSet<Map> maps) {
|
||||
CheckMapsParameters const parameters(maps);
|
||||
return new (zone()) Operator1<CheckMapsParameters>( // --
|
||||
IrOpcode::kCheckMaps, // opcode
|
||||
Operator::kNoThrow | Operator::kNoWrite, // flags
|
||||
"CheckMaps", // name
|
||||
1, 1, 1, 0, 1, 0, // counts
|
||||
parameters); // parameter
|
||||
}
|
||||
|
||||
const Operator* SimplifiedOperatorBuilder::CheckFloat64Hole(
|
||||
@ -741,7 +779,7 @@ const Operator* SimplifiedOperatorBuilder::TransitionElementsKind(
|
||||
IrOpcode::kTransitionElementsKind, // opcode
|
||||
Operator::kNoDeopt | Operator::kNoThrow, // flags
|
||||
"TransitionElementsKind", // name
|
||||
3, 1, 1, 0, 1, 0, // counts
|
||||
1, 1, 1, 0, 1, 0, // counts
|
||||
transition); // parameter
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "src/handles.h"
|
||||
#include "src/machine-type.h"
|
||||
#include "src/objects.h"
|
||||
#include "src/zone/zone-handle-set.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
@ -143,6 +144,27 @@ std::ostream& operator<<(std::ostream&, CheckForMinusZeroMode);
|
||||
|
||||
CheckForMinusZeroMode CheckMinusZeroModeOf(const Operator*) WARN_UNUSED_RESULT;
|
||||
|
||||
// A descriptor for map checks.
|
||||
class CheckMapsParameters final {
|
||||
public:
|
||||
explicit CheckMapsParameters(ZoneHandleSet<Map> const& maps) : maps_(maps) {}
|
||||
|
||||
ZoneHandleSet<Map> const& maps() const { return maps_; }
|
||||
|
||||
private:
|
||||
ZoneHandleSet<Map> const maps_;
|
||||
};
|
||||
|
||||
bool operator==(CheckMapsParameters const&, CheckMapsParameters const&);
|
||||
bool operator!=(CheckMapsParameters const&, CheckMapsParameters const&);
|
||||
|
||||
size_t hash_value(CheckMapsParameters const&);
|
||||
|
||||
std::ostream& operator<<(std::ostream&, CheckMapsParameters const&);
|
||||
|
||||
CheckMapsParameters const& CheckMapsParametersOf(Operator const*)
|
||||
WARN_UNUSED_RESULT;
|
||||
|
||||
// A descriptor for growing elements backing stores.
|
||||
enum class GrowFastElementsFlag : uint8_t {
|
||||
kNone = 0u,
|
||||
@ -160,16 +182,35 @@ GrowFastElementsFlags GrowFastElementsFlagsOf(const Operator*)
|
||||
WARN_UNUSED_RESULT;
|
||||
|
||||
// A descriptor for elements kind transitions.
|
||||
enum class ElementsTransition : uint8_t {
|
||||
kFastTransition, // simple transition, just updating the map.
|
||||
kSlowTransition // full transition, round-trip to the runtime.
|
||||
class ElementsTransition final {
|
||||
public:
|
||||
enum Mode : uint8_t {
|
||||
kFastTransition, // simple transition, just updating the map.
|
||||
kSlowTransition // full transition, round-trip to the runtime.
|
||||
};
|
||||
|
||||
ElementsTransition(Mode mode, Handle<Map> source, Handle<Map> target)
|
||||
: mode_(mode), source_(source), target_(target) {}
|
||||
|
||||
Mode mode() const { return mode_; }
|
||||
Handle<Map> source() const { return source_; }
|
||||
Handle<Map> target() const { return target_; }
|
||||
|
||||
private:
|
||||
Mode const mode_;
|
||||
Handle<Map> const source_;
|
||||
Handle<Map> const target_;
|
||||
};
|
||||
|
||||
bool operator==(ElementsTransition const&, ElementsTransition const&);
|
||||
bool operator!=(ElementsTransition const&, ElementsTransition const&);
|
||||
|
||||
size_t hash_value(ElementsTransition);
|
||||
|
||||
std::ostream& operator<<(std::ostream&, ElementsTransition);
|
||||
|
||||
ElementsTransition ElementsTransitionOf(const Operator* op) WARN_UNUSED_RESULT;
|
||||
ElementsTransition const& ElementsTransitionOf(const Operator* op)
|
||||
WARN_UNUSED_RESULT;
|
||||
|
||||
// A hint for speculative number operations.
|
||||
enum class NumberOperationHint : uint8_t {
|
||||
@ -322,7 +363,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
|
||||
|
||||
const Operator* CheckIf();
|
||||
const Operator* CheckBounds();
|
||||
const Operator* CheckMaps(int map_input_count);
|
||||
const Operator* CheckMaps(ZoneHandleSet<Map>);
|
||||
|
||||
const Operator* CheckHeapObject();
|
||||
const Operator* CheckInternalizedString();
|
||||
|
@ -956,8 +956,6 @@ void Verifier::Visitor::Check(Node* node) {
|
||||
break;
|
||||
case IrOpcode::kTransitionElementsKind:
|
||||
CheckValueInputIs(node, 0, Type::Any());
|
||||
CheckValueInputIs(node, 1, Type::Internal());
|
||||
CheckValueInputIs(node, 2, Type::Internal());
|
||||
CheckNotTyped(node);
|
||||
break;
|
||||
|
||||
|
@ -1327,6 +1327,7 @@
|
||||
'zone/zone-segment.h',
|
||||
'zone/zone-allocator.h',
|
||||
'zone/zone-containers.h',
|
||||
'zone/zone-handle-set.h',
|
||||
],
|
||||
'conditions': [
|
||||
['want_separate_host_toolset==1', {
|
||||
|
165
src/zone/zone-handle-set.h
Normal file
165
src/zone/zone-handle-set.h
Normal file
@ -0,0 +1,165 @@
|
||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef V8_ZONE_ZONE_HANDLE_SET_H_
|
||||
#define V8_ZONE_ZONE_HANDLE_SET_H_
|
||||
|
||||
#include "src/handles.h"
|
||||
#include "src/zone/zone.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
template <typename T>
|
||||
class ZoneHandleSet final {
|
||||
public:
|
||||
ZoneHandleSet() : data_(kEmptyTag) {}
|
||||
explicit ZoneHandleSet(Handle<T> handle)
|
||||
: data_(bit_cast<intptr_t>(handle.address()) | kSingletonTag) {
|
||||
DCHECK(IsAligned(bit_cast<intptr_t>(handle.address()), kPointerAlignment));
|
||||
}
|
||||
|
||||
bool is_empty() const { return data_ == kEmptyTag; }
|
||||
|
||||
size_t size() const {
|
||||
if ((data_ & kTagMask) == kEmptyTag) return 0;
|
||||
if ((data_ & kTagMask) == kSingletonTag) return 1;
|
||||
return list()->length();
|
||||
}
|
||||
|
||||
Handle<T> at(size_t i) const {
|
||||
DCHECK_NE(kEmptyTag, data_ & kTagMask);
|
||||
if ((data_ & kTagMask) == kSingletonTag) {
|
||||
DCHECK_EQ(0u, i);
|
||||
return Handle<T>(singleton());
|
||||
}
|
||||
return Handle<T>(list()->at(static_cast<int>(i)));
|
||||
}
|
||||
|
||||
Handle<T> operator[](size_t i) const { return at(i); }
|
||||
|
||||
void insert(Handle<T> handle, Zone* zone) {
|
||||
T** const value = bit_cast<T**>(handle.address());
|
||||
DCHECK(IsAligned(bit_cast<intptr_t>(value), kPointerAlignment));
|
||||
if ((data_ & kTagMask) == kEmptyTag) {
|
||||
data_ = bit_cast<intptr_t>(value) | kSingletonTag;
|
||||
} else if ((data_ & kTagMask) == kSingletonTag) {
|
||||
if (singleton() == value) return;
|
||||
List* list = new (zone) List(2, zone);
|
||||
if (singleton() < value) {
|
||||
list->Add(singleton(), zone);
|
||||
list->Add(value, zone);
|
||||
} else {
|
||||
list->Add(value, zone);
|
||||
list->Add(singleton(), zone);
|
||||
}
|
||||
DCHECK(IsAligned(bit_cast<intptr_t>(list), kPointerAlignment));
|
||||
data_ = bit_cast<intptr_t>(list) | kListTag;
|
||||
} else {
|
||||
DCHECK_EQ(kListTag, data_ & kTagMask);
|
||||
List const* const old_list = list();
|
||||
for (int i = 0; i < old_list->length(); ++i) {
|
||||
if (old_list->at(i) == value) return;
|
||||
if (old_list->at(i) > value) break;
|
||||
}
|
||||
List* new_list = new (zone) List(old_list->length() + 1, zone);
|
||||
int i = 0;
|
||||
for (; i < old_list->length(); ++i) {
|
||||
if (old_list->at(i) > value) break;
|
||||
new_list->Add(old_list->at(i), zone);
|
||||
}
|
||||
new_list->Add(value, zone);
|
||||
for (; i < old_list->length(); ++i) {
|
||||
new_list->Add(old_list->at(i), zone);
|
||||
}
|
||||
DCHECK_EQ(old_list->length() + 1, new_list->length());
|
||||
DCHECK(IsAligned(bit_cast<intptr_t>(new_list), kPointerAlignment));
|
||||
data_ = bit_cast<intptr_t>(new_list) | kListTag;
|
||||
}
|
||||
}
|
||||
|
||||
bool contains(ZoneHandleSet<T> const& other) const {
|
||||
if (data_ == other.data_) return true;
|
||||
if (data_ == kEmptyTag) return false;
|
||||
if (other.data_ == kEmptyTag) return true;
|
||||
if ((data_ & kTagMask) == kSingletonTag) return false;
|
||||
DCHECK_EQ(kListTag, data_ & kTagMask);
|
||||
if ((other.data_ & kTagMask) == kSingletonTag) {
|
||||
return list()->Contains(other.singleton());
|
||||
}
|
||||
DCHECK_EQ(kListTag, other.data_ & kTagMask);
|
||||
// TODO(bmeurer): Optimize this case.
|
||||
for (int i = 0; i < other.list()->length(); ++i) {
|
||||
if (!list()->Contains(other.list()->at(i))) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void remove(Handle<T> handle, Zone* zone) {
|
||||
// TODO(bmeurer): Optimize this case.
|
||||
ZoneHandleSet<T> that;
|
||||
for (size_t i = 0; i < size(); ++i) {
|
||||
Handle<T> value = at(i);
|
||||
if (value.address() != handle.address()) {
|
||||
that.insert(value, zone);
|
||||
}
|
||||
}
|
||||
std::swap(*this, that);
|
||||
}
|
||||
|
||||
friend bool operator==(ZoneHandleSet<T> const& lhs,
|
||||
ZoneHandleSet<T> const& rhs) {
|
||||
if (lhs.data_ == rhs.data_) return true;
|
||||
if ((lhs.data_ & kTagMask) == kListTag &&
|
||||
(rhs.data_ & kTagMask) == kListTag) {
|
||||
List const* const lhs_list = lhs.list();
|
||||
List const* const rhs_list = rhs.list();
|
||||
if (lhs_list->length() == rhs_list->length()) {
|
||||
for (int i = 0; i < lhs_list->length(); ++i) {
|
||||
if (lhs_list->at(i) != rhs_list->at(i)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
friend bool operator!=(ZoneHandleSet<T> const& lhs,
|
||||
ZoneHandleSet<T> const& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
friend size_t hash_value(ZoneHandleSet<T> const& set) {
|
||||
return static_cast<size_t>(set.data_);
|
||||
}
|
||||
|
||||
private:
|
||||
typedef ZoneList<T**> List;
|
||||
|
||||
List const* list() const {
|
||||
DCHECK_EQ(kListTag, data_ & kTagMask);
|
||||
return bit_cast<List const*>(data_ - kListTag);
|
||||
}
|
||||
|
||||
T** singleton() const {
|
||||
DCHECK_EQ(kSingletonTag, data_ & kTagMask);
|
||||
return bit_cast<T**>(data_ - kSingletonTag);
|
||||
}
|
||||
|
||||
enum Tag : intptr_t {
|
||||
kSingletonTag = 0,
|
||||
kEmptyTag = 1,
|
||||
kListTag = 2,
|
||||
kTagMask = 3
|
||||
};
|
||||
|
||||
STATIC_ASSERT(kTagMask < kPointerAlignment);
|
||||
|
||||
intptr_t data_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_ZONE_ZONE_HANDLE_SET_H_
|
Loading…
Reference in New Issue
Block a user