LCOV - code coverage report
Current view: top level - src/compiler - store-store-elimination.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 128 133 96.2 %
Date: 2017-10-20 Functions: 17 17 100.0 %

          Line data    Source code
       1             : // Copyright 2016 the V8 project authors. All rights reserved.
       2             : // Use of this source code is governed by a BSD-style license that can be
       3             : // found in the LICENSE file.
       4             : 
       5             : #include <iterator>
       6             : 
       7             : #include "src/compiler/store-store-elimination.h"
       8             : 
       9             : #include "src/compiler/all-nodes.h"
      10             : #include "src/compiler/js-graph.h"
      11             : #include "src/compiler/node-properties.h"
      12             : #include "src/compiler/simplified-operator.h"
      13             : 
      14             : namespace v8 {
      15             : namespace internal {
      16             : namespace compiler {
      17             : 
      18             : #define TRACE(fmt, ...)                                         \
      19             :   do {                                                          \
      20             :     if (FLAG_trace_store_elimination) {                         \
      21             :       PrintF("RedundantStoreFinder: " fmt "\n", ##__VA_ARGS__); \
      22             :     }                                                           \
      23             :   } while (false)
      24             : 
      25             : // CHECK_EXTRA is like CHECK, but has two or more arguments: a boolean
      26             : // expression, a format string, and any number of extra arguments. The boolean
      27             : // expression will be evaluated at runtime. If it evaluates to false, then an
      28             : // error message will be shown containing the condition, as well as the extra
      29             : // info formatted like with printf.
      30             : #define CHECK_EXTRA(condition, fmt, ...)                                 \
      31             :   do {                                                                   \
      32             :     if (V8_UNLIKELY(!(condition))) {                                     \
      33             :       V8_Fatal(__FILE__, __LINE__, "Check failed: %s. Extra info: " fmt, \
      34             :                #condition, ##__VA_ARGS__);                               \
      35             :     }                                                                    \
      36             :   } while (0)
      37             : 
      38             : #ifdef DEBUG
      39             : #define DCHECK_EXTRA(condition, fmt, ...) \
      40             :   CHECK_EXTRA(condition, fmt, ##__VA_ARGS__)
      41             : #else
      42             : #define DCHECK_EXTRA(condition, fmt, ...) ((void)0)
      43             : #endif
      44             : 
      45             : // Store-store elimination.
      46             : //
      47             : // The aim of this optimization is to detect the following pattern in the
      48             : // effect graph:
      49             : //
      50             : // - StoreField[+24, kRepTagged](263, ...)
      51             : //
      52             : //   ... lots of nodes from which the field at offset 24 of the object
      53             : //       returned by node #263 cannot be observed ...
      54             : //
      55             : // - StoreField[+24, kRepTagged](263, ...)
      56             : //
      57             : // In such situations, the earlier StoreField cannot be observed, and can be
      58             : // eliminated. This optimization should work for any offset and input node, of
      59             : // course.
      60             : //
      61             : // The optimization also works across splits. It currently does not work for
      62             : // loops, because we tend to put a stack check in loops, and like deopts,
      63             : // stack checks can observe anything.
      64             : 
      65             : // Assumption: every byte of a JS object is only ever accessed through one
      66             : // offset. For instance, byte 15 of a given object may be accessed using a
      67             : // two-byte read at offset 14, or a four-byte read at offset 12, but never
      68             : // both in the same program.
      69             : //
      70             : // This implementation needs all dead nodes removed from the graph, and the
      71             : // graph should be trimmed.
      72             : 
      73             : namespace {
      74             : 
      75             : typedef uint32_t StoreOffset;
      76             : 
      77             : struct UnobservableStore {
      78             :   NodeId id_;
      79             :   StoreOffset offset_;
      80             : 
      81             :   bool operator==(const UnobservableStore) const;
      82             :   bool operator!=(const UnobservableStore) const;
      83             :   bool operator<(const UnobservableStore) const;
      84             : };
      85             : 
      86             : }  // namespace
      87             : 
      88             : namespace {
      89             : 
      90             : // Instances of UnobservablesSet are immutable. They represent either a set of
      91             : // UnobservableStores, or the "unvisited empty set".
      92             : //
      93             : // We apply some sharing to save memory. The class UnobservablesSet is only a
      94             : // pointer wide, and a copy does not use any heap (or temp_zone) memory. Most
      95             : // changes to an UnobservablesSet might allocate in the temp_zone.
      96             : //
      97             : // The size of an instance should be the size of a pointer, plus additional
      98             : // space in the zone in the case of non-unvisited UnobservablesSets. Copying
      99             : // an UnobservablesSet allocates no memory.
     100             : class UnobservablesSet final {
     101             :  public:
     102             :   static UnobservablesSet Unvisited();
     103             :   static UnobservablesSet VisitedEmpty(Zone* zone);
     104             :   UnobservablesSet();  // unvisited
     105   122652128 :   UnobservablesSet(const UnobservablesSet& other) : set_(other.set_) {}
     106             : 
     107             :   UnobservablesSet Intersect(UnobservablesSet other, Zone* zone) const;
     108             :   UnobservablesSet Add(UnobservableStore obs, Zone* zone) const;
     109             :   UnobservablesSet RemoveSameOffset(StoreOffset off, Zone* zone) const;
     110             : 
     111     5087765 :   const ZoneSet<UnobservableStore>* set() const { return set_; }
     112             : 
     113    77109294 :   bool IsUnvisited() const { return set_ == nullptr; }
     114     5372677 :   bool IsEmpty() const { return set_ == nullptr || set_->empty(); }
     115     1238680 :   bool Contains(UnobservableStore obs) const {
     116     2477360 :     return set_ != nullptr && (set_->find(obs) != set_->end());
     117             :   }
     118             : 
     119             :   bool operator==(const UnobservablesSet&) const;
     120             :   bool operator!=(const UnobservablesSet&) const;
     121             : 
     122             :  private:
     123             :   explicit UnobservablesSet(const ZoneSet<UnobservableStore>* set)
     124     3341756 :       : set_(set) {}
     125             :   const ZoneSet<UnobservableStore>* set_;
     126             : };
     127             : 
     128             : }  // namespace
     129             : 
     130             : namespace {
     131             : 
     132             : class RedundantStoreFinder final {
     133             :  public:
     134             :   RedundantStoreFinder(JSGraph* js_graph, Zone* temp_zone);
     135             : 
     136             :   void Find();
     137             : 
     138             :   const ZoneSet<Node*>& to_remove_const() { return to_remove_; }
     139             : 
     140             :   void Visit(Node* node);
     141             : 
     142             :  private:
     143             :   static bool IsEffectful(Node* node);
     144             :   void VisitEffectfulNode(Node* node);
     145             :   UnobservablesSet RecomputeUseIntersection(Node* node);
     146             :   UnobservablesSet RecomputeSet(Node* node, UnobservablesSet uses);
     147             :   static bool CannotObserveStoreField(Node* node);
     148             : 
     149             :   void MarkForRevisit(Node* node);
     150             :   bool HasBeenVisited(Node* node);
     151             : 
     152             :   JSGraph* jsgraph() const { return jsgraph_; }
     153             :   Zone* temp_zone() const { return temp_zone_; }
     154             :   ZoneVector<UnobservablesSet>& unobservable() { return unobservable_; }
     155             :   UnobservablesSet& unobservable_for_id(NodeId id) {
     156             :     DCHECK_LT(id, unobservable().size());
     157    98687872 :     return unobservable()[id];
     158             :   }
     159             :   ZoneSet<Node*>& to_remove() { return to_remove_; }
     160             : 
     161             :   JSGraph* const jsgraph_;
     162             :   Zone* const temp_zone_;
     163             : 
     164             :   ZoneStack<Node*> revisit_;
     165             :   ZoneVector<bool> in_revisit_;
     166             :   // Maps node IDs to UnobservableNodeSets.
     167             :   ZoneVector<UnobservablesSet> unobservable_;
     168             :   ZoneSet<Node*> to_remove_;
     169             :   const UnobservablesSet unobservables_visited_empty_;
     170             : };
     171             : 
     172             : // To safely cast an offset from a FieldAccess, which has a potentially wider
     173             : // range (namely int).
     174             : StoreOffset ToOffset(int offset) {
     175     2945302 :   CHECK_LE(0, offset);
     176     2945302 :   return static_cast<StoreOffset>(offset);
     177             : }
     178             : 
     179             : StoreOffset ToOffset(const FieldAccess& access) {
     180             :   return ToOffset(access.offset);
     181             : }
     182             : 
     183             : unsigned int RepSizeOf(MachineRepresentation rep) {
     184     1241303 :   return 1u << ElementSizeLog2Of(rep);
     185             : }
     186             : unsigned int RepSizeOf(FieldAccess access) {
     187             :   return RepSizeOf(access.machine_type.representation());
     188             : }
     189             : 
     190       51586 : bool AtMostTagged(FieldAccess access) {
     191       51586 :   return RepSizeOf(access) <= RepSizeOf(MachineRepresentation::kTagged);
     192             : }
     193             : 
     194     1189717 : bool AtLeastTagged(FieldAccess access) {
     195     1189717 :   return RepSizeOf(access) >= RepSizeOf(MachineRepresentation::kTagged);
     196             : }
     197             : 
     198             : }  // namespace
     199             : 
     200      443352 : void RedundantStoreFinder::Find() {
     201      443352 :   Visit(jsgraph()->graph()->end());
     202             : 
     203    18867164 :   while (!revisit_.empty()) {
     204    35960909 :     Node* next = revisit_.top();
     205             :     revisit_.pop();
     206             :     DCHECK_LT(next->id(), in_revisit_.size());
     207    17980456 :     in_revisit_[next->id()] = false;
     208    17980456 :     Visit(next);
     209             :   }
     210             : 
     211             : #ifdef DEBUG
     212             :   // Check that we visited all the StoreFields
     213             :   AllNodes all(temp_zone(), jsgraph()->graph());
     214             :   for (Node* node : all.reachable) {
     215             :     if (node->op()->opcode() == IrOpcode::kStoreField) {
     216             :       DCHECK_EXTRA(HasBeenVisited(node), "#%d:%s", node->id(),
     217             :                    node->op()->mnemonic());
     218             :     }
     219             :   }
     220             : #endif
     221      443360 : }
     222             : 
     223    23914927 : void RedundantStoreFinder::MarkForRevisit(Node* node) {
     224             :   DCHECK_LT(node->id(), in_revisit_.size());
     225   107705763 :   if (!in_revisit_[node->id()]) {
     226             :     revisit_.push(node);
     227    35960982 :     in_revisit_[node->id()] = true;
     228             :   }
     229    23914937 : }
     230             : 
     231    65214911 : bool RedundantStoreFinder::HasBeenVisited(Node* node) {
     232             :   return !unobservable_for_id(node->id()).IsUnvisited();
     233             : }
     234             : 
     235      443350 : void StoreStoreElimination::Run(JSGraph* js_graph, Zone* temp_zone) {
     236             :   // Find superfluous nodes
     237      443350 :   RedundantStoreFinder finder(js_graph, temp_zone);
     238      443353 :   finder.Find();
     239             : 
     240             :   // Remove superfluous nodes
     241             : 
     242      938295 :   for (Node* node : finder.to_remove_const()) {
     243       51586 :     if (FLAG_trace_store_elimination) {
     244             :       PrintF("StoreStoreElimination::Run: Eliminating node #%d:%s\n",
     245           0 :              node->id(), node->op()->mnemonic());
     246             :     }
     247       51586 :     Node* previous_effect = NodeProperties::GetEffectInput(node);
     248             :     NodeProperties::ReplaceUses(node, nullptr, previous_effect, nullptr,
     249       51586 :                                 nullptr);
     250       51586 :     node->Kill();
     251             :   }
     252      443357 : }
     253             : 
     254             : bool RedundantStoreFinder::IsEffectful(Node* node) {
     255             :   return (node->op()->EffectInputCount() >= 1);
     256             : }
     257             : 
     258             : // Recompute unobservables-set for a node. Will also mark superfluous nodes
     259             : // as to be removed.
     260             : 
     261    10619282 : UnobservablesSet RedundantStoreFinder::RecomputeSet(Node* node,
     262     2891093 :                                                     UnobservablesSet uses) {
     263    10619282 :   switch (node->op()->opcode()) {
     264             :     case IrOpcode::kStoreField: {
     265     1238681 :       Node* stored_to = node->InputAt(0);
     266     1238681 :       FieldAccess access = OpParameter<FieldAccess>(node->op());
     267             :       StoreOffset offset = ToOffset(access);
     268             : 
     269     1238681 :       UnobservableStore observation = {stored_to->id(), offset};
     270     1238681 :       bool isNotObservable = uses.Contains(observation);
     271             : 
     272     1238680 :       if (isNotObservable && AtMostTagged(access)) {
     273       51586 :         TRACE("  #%d is StoreField[+%d,%s](#%d), unobservable", node->id(),
     274             :               offset, MachineReprToString(access.machine_type.representation()),
     275             :               stored_to->id());
     276             :         to_remove().insert(node);
     277     1238680 :         return uses;
     278     1187094 :       } else if (isNotObservable && !AtMostTagged(access)) {
     279           0 :         TRACE(
     280             :             "  #%d is StoreField[+%d,%s](#%d), repeated in future but too "
     281             :             "big to optimize away",
     282             :             node->id(), offset,
     283             :             MachineReprToString(access.machine_type.representation()),
     284             :             stored_to->id());
     285             :         return uses;
     286     1187094 :       } else if (!isNotObservable && AtLeastTagged(access)) {
     287     1184472 :         TRACE("  #%d is StoreField[+%d,%s](#%d), observable, recording in set",
     288             :               node->id(), offset,
     289             :               MachineReprToString(access.machine_type.representation()),
     290             :               stored_to->id());
     291     1184472 :         return uses.Add(observation, temp_zone());
     292        2623 :       } else if (!isNotObservable && !AtLeastTagged(access)) {
     293        2623 :         TRACE(
     294             :             "  #%d is StoreField[+%d,%s](#%d), observable but too small to "
     295             :             "record",
     296             :             node->id(), offset,
     297             :             MachineReprToString(access.machine_type.representation()),
     298             :             stored_to->id());
     299             :         return uses;
     300             :       } else {
     301           0 :         UNREACHABLE();
     302             :       }
     303             :       break;
     304             :     }
     305             :     case IrOpcode::kLoadField: {
     306           0 :       Node* loaded_from = node->InputAt(0);
     307     1706621 :       FieldAccess access = OpParameter<FieldAccess>(node->op());
     308             :       StoreOffset offset = ToOffset(access);
     309             : 
     310     1706621 :       TRACE(
     311             :           "  #%d is LoadField[+%d,%s](#%d), removing all offsets [+%d] from "
     312             :           "set",
     313             :           node->id(), offset,
     314             :           MachineReprToString(access.machine_type.representation()),
     315             :           loaded_from->id(), offset);
     316             : 
     317     1706621 :       return uses.RemoveSameOffset(offset, temp_zone());
     318             :       break;
     319             :     }
     320             :     default:
     321     7673980 :       if (CannotObserveStoreField(node)) {
     322     2272271 :         TRACE("  #%d:%s can observe nothing, set stays unchanged", node->id(),
     323             :               node->op()->mnemonic());
     324             :         return uses;
     325             :       } else {
     326     5401723 :         TRACE("  #%d:%s might observe anything, recording empty set",
     327             :               node->id(), node->op()->mnemonic());
     328             :         return unobservables_visited_empty_;
     329             :       }
     330             :   }
     331             :   UNREACHABLE();
     332             : }
     333             : 
     334     7673982 : bool RedundantStoreFinder::CannotObserveStoreField(Node* node) {
     335     7673985 :   return node->opcode() == IrOpcode::kCheckedLoad ||
     336     7639956 :          node->opcode() == IrOpcode::kLoadElement ||
     337     6727635 :          node->opcode() == IrOpcode::kLoad ||
     338     6727163 :          node->opcode() == IrOpcode::kStore ||
     339     5468416 :          node->opcode() == IrOpcode::kEffectPhi ||
     340     5420502 :          node->opcode() == IrOpcode::kStoreElement ||
     341     5420502 :          node->opcode() == IrOpcode::kCheckedStore ||
     342    13087898 :          node->opcode() == IrOpcode::kUnsafePointerAdd ||
     343     7673982 :          node->opcode() == IrOpcode::kRetain;
     344             : }
     345             : 
     346             : // Initialize unobservable_ with js_graph->graph->NodeCount() empty sets.
     347     1330064 : RedundantStoreFinder::RedundantStoreFinder(JSGraph* js_graph, Zone* temp_zone)
     348             :     : jsgraph_(js_graph),
     349             :       temp_zone_(temp_zone),
     350             :       revisit_(temp_zone),
     351             :       in_revisit_(js_graph->graph()->NodeCount(), temp_zone),
     352             :       unobservable_(js_graph->graph()->NodeCount(),
     353             :                     UnobservablesSet::Unvisited(), temp_zone),
     354             :       to_remove_(temp_zone),
     355     2216784 :       unobservables_visited_empty_(UnobservablesSet::VisitedEmpty(temp_zone)) {}
     356             : 
     357    71639229 : void RedundantStoreFinder::Visit(Node* node) {
     358             :   // All effectful nodes should be reachable from End via a sequence of
     359             :   // control, then a sequence of effect edges. In VisitEffectfulNode we mark
     360             :   // all effect inputs for revisiting (if they might have stale state); here
     361             :   // we mark all control inputs at least once.
     362             : 
     363    18423834 :   if (!HasBeenVisited(node)) {
     364    87331051 :     for (int i = 0; i < node->op()->ControlInputCount(); i++) {
     365    17747917 :       Node* control_input = NodeProperties::GetControlInput(node, i);
     366    17747928 :       if (!HasBeenVisited(control_input)) {
     367    12807869 :         MarkForRevisit(control_input);
     368             :       }
     369             :     }
     370             :   }
     371             : 
     372    18423822 :   bool isEffectful = (node->op()->EffectInputCount() >= 1);
     373    18423822 :   if (isEffectful) {
     374    10619296 :     VisitEffectfulNode(node);
     375             :     DCHECK(HasBeenVisited(node));
     376             :   }
     377             : 
     378    18423856 :   if (!HasBeenVisited(node)) {
     379             :     // Mark as visited.
     380     7421970 :     unobservable_for_id(node->id()) = unobservables_visited_empty_;
     381             :   }
     382    18423856 : }
     383             : 
     384    62209042 : void RedundantStoreFinder::VisitEffectfulNode(Node* node) {
     385    10619293 :   if (HasBeenVisited(node)) {
     386      997588 :     TRACE("- Revisiting: #%d:%s", node->id(), node->op()->mnemonic());
     387             :   }
     388    10619293 :   UnobservablesSet after_set = RecomputeUseIntersection(node);
     389    10619294 :   UnobservablesSet before_set = RecomputeSet(node, after_set);
     390             :   DCHECK(!before_set.IsUnvisited());
     391             : 
     392             :   UnobservablesSet stored_for_node = unobservable_for_id(node->id());
     393             :   bool cur_set_changed =
     394    11616874 :       (stored_for_node.IsUnvisited() || stored_for_node != before_set);
     395    10619285 :   if (!cur_set_changed) {
     396             :     // We will not be able to update the part of this chain above any more.
     397             :     // Exit.
     398      997261 :     TRACE("+ No change: stabilized. Not visiting effect inputs.");
     399             :   } else {
     400     9622024 :     unobservable_for_id(node->id()) = before_set;
     401             : 
     402             :     // Mark effect inputs for visiting.
     403    62187432 :     for (int i = 0; i < node->op()->EffectInputCount(); i++) {
     404    11107112 :       Node* input = NodeProperties::GetEffectInput(node, i);
     405    11107120 :       TRACE("    marking #%d:%s for revisit", input->id(),
     406             :             input->op()->mnemonic());
     407    11107120 :       MarkForRevisit(input);
     408             :     }
     409             :   }
     410    10619293 : }
     411             : 
     412             : // Compute the intersection of the UnobservablesSets of all effect uses and
     413             : // return it. This function only works if {node} has an effect use.
     414             : //
     415             : // The result UnobservablesSet will always be visited.
     416    13951730 : UnobservablesSet RedundantStoreFinder::RecomputeUseIntersection(Node* node) {
     417             :   // {first} == true indicates that we haven't looked at any elements yet.
     418             :   // {first} == false indicates that cur_set is the intersection of at least one
     419             :   // thing.
     420             : 
     421             :   bool first = true;
     422             :   UnobservablesSet cur_set = UnobservablesSet::Unvisited();  // irrelevant
     423             : 
     424    79336078 :   for (Edge edge : node->use_edges()) {
     425             :     // Skip non-effect edges
     426    34358395 :     if (!NodeProperties::IsEffectEdge(edge)) {
     427             :       continue;
     428             :     }
     429             : 
     430    13231649 :     Node* use = edge.from();
     431             :     UnobservablesSet new_set = unobservable_for_id(use->id());
     432             :     // Include new_set in the intersection.
     433    13231649 :     if (first) {
     434             :       // Intersection of a one-element set is that one element
     435             :       first = false;
     436     9899205 :       cur_set = new_set;
     437             :     } else {
     438             :       // Take the intersection of cur_set and new_set.
     439     3332444 :       cur_set = cur_set.Intersect(new_set, temp_zone());
     440             :     }
     441             :   }
     442             : 
     443    10619288 :   if (first) {
     444             :     // There were no effect uses.
     445             :     auto opcode = node->op()->opcode();
     446             :     // List of opcodes that may end this effect chain. The opcodes are not
     447             :     // important to the soundness of this optimization; this serves as a
     448             :     // general sanity check. Add opcodes to this list as it suits you.
     449             :     //
     450             :     // Everything is observable after these opcodes; return the empty set.
     451             :     DCHECK_EXTRA(
     452             :         opcode == IrOpcode::kReturn || opcode == IrOpcode::kTerminate ||
     453             :             opcode == IrOpcode::kDeoptimize || opcode == IrOpcode::kThrow,
     454             :         "for #%d:%s", node->id(), node->op()->mnemonic());
     455             :     USE(opcode);  // silence warning about unused variable in release mode
     456             : 
     457             :     return unobservables_visited_empty_;
     458             :   } else {
     459     9899205 :     if (cur_set.IsUnvisited()) {
     460     2436352 :       cur_set = unobservables_visited_empty_;
     461             :     }
     462             : 
     463             :     return cur_set;
     464             :   }
     465             : }
     466             : 
     467             : UnobservablesSet UnobservablesSet::Unvisited() { return UnobservablesSet(); }
     468             : 
     469    14387779 : UnobservablesSet::UnobservablesSet() : set_(nullptr) {}
     470             : 
     471      443355 : UnobservablesSet UnobservablesSet::VisitedEmpty(Zone* zone) {
     472             :   // Create a new empty UnobservablesSet. This allocates in the zone, and
     473             :   // can probably be optimized to use a global singleton.
     474             :   ZoneSet<UnobservableStore>* empty_set =
     475             :       new (zone->New(sizeof(ZoneSet<UnobservableStore>)))
     476      443355 :           ZoneSet<UnobservableStore>(zone);
     477      443357 :   return UnobservablesSet(empty_set);
     478             : }
     479             : 
     480             : // Computes the intersection of two UnobservablesSets. May return
     481             : // UnobservablesSet::Unvisited() instead of an empty UnobservablesSet for
     482             : // speed.
     483     3332442 : UnobservablesSet UnobservablesSet::Intersect(UnobservablesSet other,
     484             :                                              Zone* zone) const {
     485     3367092 :   if (IsEmpty() || other.IsEmpty()) {
     486             :     return Unvisited();
     487             :   } else {
     488             :     ZoneSet<UnobservableStore>* intersection =
     489             :         new (zone->New(sizeof(ZoneSet<UnobservableStore>)))
     490        7305 :             ZoneSet<UnobservableStore>(zone);
     491             :     // Put the intersection of set() and other.set() in intersection.
     492             :     set_intersection(set()->begin(), set()->end(), other.set()->begin(),
     493             :                      other.set()->end(),
     494        7305 :                      std::inserter(*intersection, intersection->end()));
     495             : 
     496             :     return UnobservablesSet(intersection);
     497             :   }
     498             : }
     499             : 
     500     1184471 : UnobservablesSet UnobservablesSet::Add(UnobservableStore obs,
     501             :                                        Zone* zone) const {
     502             :   bool present = (set()->find(obs) != set()->end());
     503     1184471 :   if (present) {
     504             :     return *this;
     505             :   } else {
     506             :     // Make a new empty set.
     507             :     ZoneSet<UnobservableStore>* new_set =
     508             :         new (zone->New(sizeof(ZoneSet<UnobservableStore>)))
     509     1184471 :             ZoneSet<UnobservableStore>(zone);
     510             :     // Copy the old elements over.
     511             :     *new_set = *set();
     512             :     // Add the new element.
     513             :     bool inserted = new_set->insert(obs).second;
     514             :     DCHECK(inserted);
     515             :     USE(inserted);  // silence warning about unused variable
     516             : 
     517             :     return UnobservablesSet(new_set);
     518             :   }
     519             : }
     520             : 
     521     1706622 : UnobservablesSet UnobservablesSet::RemoveSameOffset(StoreOffset offset,
     522             :                                                     Zone* zone) const {
     523             :   // Make a new empty set.
     524             :   ZoneSet<UnobservableStore>* new_set =
     525             :       new (zone->New(sizeof(ZoneSet<UnobservableStore>)))
     526     1706622 :           ZoneSet<UnobservableStore>(zone);
     527             :   // Copy all elements over that have a different offset.
     528     9596794 :   for (auto obs : *set()) {
     529     6183547 :     if (obs.offset_ != offset) {
     530             :       new_set->insert(obs);
     531             :     }
     532             :   }
     533             : 
     534     1706623 :   return UnobservablesSet(new_set);
     535             : }
     536             : 
     537             : // Used for debugging.
     538      997589 : bool UnobservablesSet::operator==(const UnobservablesSet& other) const {
     539     1995178 :   if (IsUnvisited() || other.IsUnvisited()) {
     540           0 :     return IsEmpty() && other.IsEmpty();
     541             :   } else {
     542             :     // Both pointers guaranteed not to be nullptrs.
     543      997586 :     return *set() == *other.set();
     544             :   }
     545             : }
     546             : 
     547             : bool UnobservablesSet::operator!=(const UnobservablesSet& other) const {
     548      997589 :   return !(*this == other);
     549             : }
     550             : 
     551             : bool UnobservableStore::operator==(const UnobservableStore other) const {
     552       23597 :   return (id_ == other.id_) && (offset_ == other.offset_);
     553             : }
     554             : 
     555             : bool UnobservableStore::operator!=(const UnobservableStore other) const {
     556             :   return !(*this == other);
     557             : }
     558             : 
     559             : bool UnobservableStore::operator<(const UnobservableStore other) const {
     560   112780562 :   return (id_ < other.id_) || (id_ == other.id_ && offset_ < other.offset_);
     561             : }
     562             : 
     563             : #undef TRACE
     564             : #undef CHECK_EXTRA
     565             : #undef DCHECK_EXTRA
     566             : 
     567             : }  // namespace compiler
     568             : }  // namespace internal
     569             : }  // namespace v8

Generated by: LCOV version 1.10