Commit 45aa1351 authored by bmeurer's avatar bmeurer Committed by Commit bot

[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}
parent 09cb6efd
......@@ -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(
......
This diff is collapsed.
......@@ -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', {
......
// 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_
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