LCOV - code coverage report
Current view: top level - src/compiler - escape-analysis-reducer.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 168 171 98.2 %
Date: 2019-04-17 Functions: 13 14 92.9 %

          Line data    Source code
       1             : // Copyright 2017 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 "src/compiler/escape-analysis-reducer.h"
       6             : 
       7             : #include "src/compiler/all-nodes.h"
       8             : #include "src/compiler/simplified-operator.h"
       9             : #include "src/compiler/type-cache.h"
      10             : #include "src/frame-constants.h"
      11             : 
      12             : namespace v8 {
      13             : namespace internal {
      14             : namespace compiler {
      15             : 
      16             : #ifdef DEBUG
      17             : #define TRACE(...)                                    \
      18             :   do {                                                \
      19             :     if (FLAG_trace_turbo_escape) PrintF(__VA_ARGS__); \
      20             :   } while (false)
      21             : #else
      22             : #define TRACE(...)
      23             : #endif  // DEBUG
      24             : 
      25      464162 : EscapeAnalysisReducer::EscapeAnalysisReducer(
      26             :     Editor* editor, JSGraph* jsgraph, EscapeAnalysisResult analysis_result,
      27             :     Zone* zone)
      28             :     : AdvancedReducer(editor),
      29             :       jsgraph_(jsgraph),
      30             :       analysis_result_(analysis_result),
      31             :       object_id_cache_(zone),
      32             :       node_cache_(jsgraph->graph(), zone),
      33             :       arguments_elements_(zone),
      34     1392488 :       zone_(zone) {}
      35             : 
      36      201363 : Reduction EscapeAnalysisReducer::ReplaceNode(Node* original,
      37             :                                              Node* replacement) {
      38             :   const VirtualObject* vobject =
      39      201363 :       analysis_result().GetVirtualObject(replacement);
      40      402726 :   if (replacement->opcode() == IrOpcode::kDead ||
      41        1454 :       (vobject && !vobject->HasEscaped())) {
      42             :     RelaxEffectsAndControls(original);
      43             :     return Replace(replacement);
      44             :   }
      45        6942 :   Type const replacement_type = NodeProperties::GetType(replacement);
      46             :   Type const original_type = NodeProperties::GetType(original);
      47        6942 :   if (replacement_type.Is(original_type)) {
      48             :     RelaxEffectsAndControls(original);
      49             :     return Replace(replacement);
      50             :   }
      51             : 
      52             :   // We need to guard the replacement if we would widen the type otherwise.
      53             :   DCHECK_EQ(1, original->op()->EffectOutputCount());
      54             :   DCHECK_EQ(1, original->op()->EffectInputCount());
      55             :   DCHECK_EQ(1, original->op()->ControlInputCount());
      56        1453 :   Node* effect = NodeProperties::GetEffectInput(original);
      57        1453 :   Node* control = NodeProperties::GetControlInput(original);
      58        1453 :   original->TrimInputCount(0);
      59        1453 :   original->AppendInput(jsgraph()->zone(), replacement);
      60        1453 :   original->AppendInput(jsgraph()->zone(), effect);
      61        1453 :   original->AppendInput(jsgraph()->zone(), control);
      62        1453 :   NodeProperties::SetType(
      63             :       original,
      64             :       Type::Intersect(original_type, replacement_type, jsgraph()->zone()));
      65        1453 :   NodeProperties::ChangeOp(original,
      66        1453 :                            jsgraph()->common()->TypeGuard(original_type));
      67             :   ReplaceWithValue(original, original, original, control);
      68             :   return NoChange();
      69             : }
      70             : 
      71             : namespace {
      72             : 
      73             : Node* SkipTypeGuards(Node* node) {
      74    52851618 :   while (node->opcode() == IrOpcode::kTypeGuard) {
      75         996 :     node = NodeProperties::GetValueInput(node, 0);
      76             :   }
      77             :   return node;
      78             : }
      79             : 
      80             : }  // namespace
      81             : 
      82       23241 : Node* EscapeAnalysisReducer::ObjectIdNode(const VirtualObject* vobject) {
      83             :   VirtualObject::Id id = vobject->id();
      84       46482 :   if (id >= object_id_cache_.size()) object_id_cache_.resize(id + 1);
      85       23241 :   if (!object_id_cache_[id]) {
      86        3686 :     Node* node = jsgraph()->graph()->NewNode(jsgraph()->common()->ObjectId(id));
      87             :     NodeProperties::SetType(node, Type::Object());
      88        3686 :     object_id_cache_[id] = node;
      89             :   }
      90       23241 :   return object_id_cache_[id];
      91             : }
      92             : 
      93    30931477 : Reduction EscapeAnalysisReducer::Reduce(Node* node) {
      94    30931477 :   if (Node* replacement = analysis_result().GetReplacementOf(node)) {
      95             :     DCHECK(node->opcode() != IrOpcode::kAllocate &&
      96             :            node->opcode() != IrOpcode::kFinishRegion);
      97             :     DCHECK_NE(replacement, node);
      98      201363 :     return ReplaceNode(node, replacement);
      99             :   }
     100             : 
     101    30730177 :   switch (node->opcode()) {
     102             :     case IrOpcode::kAllocate:
     103             :     case IrOpcode::kTypeGuard: {
     104      192900 :       const VirtualObject* vobject = analysis_result().GetVirtualObject(node);
     105      192900 :       if (vobject && !vobject->HasEscaped()) {
     106       57774 :         RelaxEffectsAndControls(node);
     107             :       }
     108             :       return NoChange();
     109             :     }
     110             :     case IrOpcode::kFinishRegion: {
     111      193245 :       Node* effect = NodeProperties::GetEffectInput(node, 0);
     112      193245 :       if (effect->opcode() == IrOpcode::kBeginRegion) {
     113             :         RelaxEffectsAndControls(effect);
     114       30031 :         RelaxEffectsAndControls(node);
     115             :       }
     116             :       return NoChange();
     117             :     }
     118             :     case IrOpcode::kNewArgumentsElements:
     119             :       arguments_elements_.insert(node);
     120             :       return NoChange();
     121             :     default: {
     122             :       // TODO(sigurds): Change this to GetFrameStateInputCount once
     123             :       // it is working. For now we use EffectInputCount > 0 to determine
     124             :       // whether a node might have a frame state input.
     125    30326292 :       if (node->op()->EffectInputCount() > 0) {
     126    11839208 :         ReduceFrameStateInputs(node);
     127             :       }
     128             :       return NoChange();
     129             :     }
     130             :   }
     131             : }
     132             : 
     133             : // While doing DFS on the FrameState tree, we have to recognize duplicate
     134             : // occurrences of virtual objects.
     135     5778948 : class Deduplicator {
     136             :  public:
     137             :   explicit Deduplicator(Zone* zone) : is_duplicate_(zone) {}
     138      136023 :   bool SeenBefore(const VirtualObject* vobject) {
     139             :     VirtualObject::Id id = vobject->id();
     140      272046 :     if (id >= is_duplicate_.size()) {
     141      101530 :       is_duplicate_.resize(id + 1);
     142             :     }
     143             :     bool is_duplicate = is_duplicate_[id];
     144             :     is_duplicate_[id] = true;
     145      136023 :     return is_duplicate;
     146             :   }
     147             : 
     148             :  private:
     149             :   ZoneVector<bool> is_duplicate_;
     150             : };
     151             : 
     152    11839219 : void EscapeAnalysisReducer::ReduceFrameStateInputs(Node* node) {
     153             :   DCHECK_GE(node->op()->EffectInputCount(), 1);
     154   103153789 :   for (int i = 0; i < node->InputCount(); ++i) {
     155             :     Node* input = node->InputAt(i);
     156    45657300 :     if (input->opcode() == IrOpcode::kFrameState) {
     157             :       Deduplicator deduplicator(zone());
     158     5778963 :       if (Node* ret = ReduceDeoptState(input, node, &deduplicator)) {
     159     5778946 :         node->ReplaceInput(i, ret);
     160             :       }
     161             :     }
     162             :   }
     163    11839204 : }
     164             : 
     165    72583425 : Node* EscapeAnalysisReducer::ReduceDeoptState(Node* node, Node* effect,
     166             :                                               Deduplicator* deduplicator) {
     167    72583425 :   if (node->opcode() == IrOpcode::kFrameState) {
     168     6233023 :     NodeHashCache::Constructor new_node(&node_cache_, node);
     169             :     // This input order is important to match the DFS traversal used in the
     170             :     // instruction selector. Otherwise, the instruction selector might find a
     171             :     // duplicate node before the original one.
     172    81025803 :     for (int input_id : {kFrameStateOuterStateInput, kFrameStateFunctionInput,
     173             :                          kFrameStateParametersInput, kFrameStateContextInput,
     174    37396497 :                          kFrameStateLocalsInput, kFrameStateStackInput}) {
     175             :       Node* input = node->InputAt(input_id);
     176    37396497 :       new_node.ReplaceInput(ReduceDeoptState(input, effect, deduplicator),
     177    37396408 :                             input_id);
     178             :     }
     179     6232916 :     return new_node.Get();
     180    66350402 :   } else if (node->opcode() == IrOpcode::kStateValues) {
     181    13499767 :     NodeHashCache::Constructor new_node(&node_cache_, node);
     182    70963263 :     for (int i = 0; i < node->op()->ValueInputCount(); ++i) {
     183    28731789 :       Node* input = NodeProperties::GetValueInput(node, i);
     184    28731612 :       new_node.ReplaceValueInput(ReduceDeoptState(input, effect, deduplicator),
     185    28731856 :                                  i);
     186             :     }
     187    13499726 :     return new_node.Get();
     188    52850526 :   } else if (const VirtualObject* vobject =
     189    52850622 :                  analysis_result().GetVirtualObject(SkipTypeGuards(node))) {
     190     1070186 :     if (vobject->HasEscaped()) return node;
     191      136023 :     if (deduplicator->SeenBefore(vobject)) {
     192       23241 :       return ObjectIdNode(vobject);
     193             :     } else {
     194             :       std::vector<Node*> inputs;
     195     1465844 :       for (int offset = 0; offset < vobject->size(); offset += kTaggedSize) {
     196             :         Node* field =
     197      676531 :             analysis_result().GetVirtualObjectField(vobject, offset, effect);
     198      676531 :         CHECK_NOT_NULL(field);
     199      676531 :         if (field != jsgraph()->Dead()) {
     200     1353062 :           inputs.push_back(ReduceDeoptState(field, effect, deduplicator));
     201             :         }
     202             :       }
     203      112782 :       int num_inputs = static_cast<int>(inputs.size());
     204             :       NodeHashCache::Constructor new_node(
     205             :           &node_cache_,
     206             :           jsgraph()->common()->ObjectState(vobject->id(), num_inputs),
     207      112782 :           num_inputs, &inputs.front(), NodeProperties::GetType(node));
     208      112782 :       return new_node.Get();
     209             :     }
     210             :   } else {
     211             :     return node;
     212             :   }
     213             : }
     214             : 
     215      464161 : void EscapeAnalysisReducer::VerifyReplacement() const {
     216      464161 :   AllNodes all(zone(), jsgraph()->graph());
     217    31014320 :   for (Node* node : all.reachable) {
     218    30550152 :     if (node->opcode() == IrOpcode::kAllocate) {
     219      113538 :       if (const VirtualObject* vobject =
     220      113538 :               analysis_result().GetVirtualObject(node)) {
     221       84186 :         if (!vobject->HasEscaped()) {
     222             :           FATAL("Escape analysis failed to remove node %s#%d\n",
     223           0 :                 node->op()->mnemonic(), node->id());
     224             :         }
     225             :       }
     226             :     }
     227             :   }
     228      464168 : }
     229             : 
     230      465465 : void EscapeAnalysisReducer::Finalize() {
     231      484514 :   for (Node* node : arguments_elements_) {
     232       19049 :     int mapped_count = NewArgumentsElementsMappedCountOf(node->op());
     233             : 
     234       19049 :     Node* arguments_frame = NodeProperties::GetValueInput(node, 0);
     235       19049 :     if (arguments_frame->opcode() != IrOpcode::kArgumentsFrame) continue;
     236       19049 :     Node* arguments_length = NodeProperties::GetValueInput(node, 1);
     237       19049 :     if (arguments_length->opcode() != IrOpcode::kArgumentsLength) continue;
     238             : 
     239             :     // If mapped arguments are specified, then their number is always equal to
     240             :     // the number of formal parameters. This allows to use just the three-value
     241             :     // {ArgumentsStateType} enum because the deoptimizer can reconstruct the
     242             :     // value of {mapped_count} from the number of formal parameters.
     243             :     DCHECK_IMPLIES(
     244             :         mapped_count != 0,
     245             :         mapped_count == FormalParameterCountOf(arguments_length->op()));
     246       19049 :     ArgumentsStateType type = IsRestLengthOf(arguments_length->op())
     247             :                                   ? ArgumentsStateType::kRestParameter
     248             :                                   : (mapped_count == 0)
     249             :                                         ? ArgumentsStateType::kUnmappedArguments
     250       19049 :                                         : ArgumentsStateType::kMappedArguments;
     251             : 
     252             :     Node* arguments_length_state = nullptr;
     253      106655 :     for (Edge edge : arguments_length->use_edges()) {
     254             :       Node* use = edge.from();
     255             :       switch (use->opcode()) {
     256             :         case IrOpcode::kObjectState:
     257             :         case IrOpcode::kTypedObjectState:
     258             :         case IrOpcode::kStateValues:
     259             :         case IrOpcode::kTypedStateValues:
     260        1437 :           if (!arguments_length_state) {
     261        1303 :             arguments_length_state = jsgraph()->graph()->NewNode(
     262             :                 jsgraph()->common()->ArgumentsLengthState(type));
     263             :             NodeProperties::SetType(arguments_length_state,
     264             :                                     Type::OtherInternal());
     265             :           }
     266        1437 :           edge.UpdateTo(arguments_length_state);
     267        1437 :           break;
     268             :         default:
     269             :           break;
     270             :       }
     271             :     }
     272             : 
     273             :     bool escaping_use = false;
     274             :     ZoneVector<Node*> loads(zone());
     275       51096 :     for (Edge edge : node->use_edges()) {
     276       24235 :       Node* use = edge.from();
     277       29668 :       if (!NodeProperties::IsValueEdge(edge)) continue;
     278       18806 :       if (use->use_edges().empty()) {
     279             :         // A node without uses is dead, so we don't have to care about it.
     280             :         continue;
     281             :       }
     282       18802 :       switch (use->opcode()) {
     283             :         case IrOpcode::kStateValues:
     284             :         case IrOpcode::kTypedStateValues:
     285             :         case IrOpcode::kObjectState:
     286             :         case IrOpcode::kTypedObjectState:
     287             :           break;
     288             :         case IrOpcode::kLoadElement:
     289         586 :           if (mapped_count == 0) {
     290         586 :             loads.push_back(use);
     291             :           } else {
     292             :             escaping_use = true;
     293             :           }
     294             :           break;
     295             :         case IrOpcode::kLoadField:
     296         497 :           if (FieldAccessOf(use->op()).offset == FixedArray::kLengthOffset) {
     297         497 :             loads.push_back(use);
     298             :           } else {
     299             :             escaping_use = true;
     300             :           }
     301             :           break;
     302             :         default:
     303             :           // If the arguments elements node node is used by an unhandled node,
     304             :           // then we cannot remove this allocation.
     305             :           escaping_use = true;
     306       16423 :           break;
     307             :       }
     308       18802 :       if (escaping_use) break;
     309             :     }
     310       19049 :     if (!escaping_use) {
     311        2626 :       Node* arguments_elements_state = jsgraph()->graph()->NewNode(
     312             :           jsgraph()->common()->ArgumentsElementsState(type));
     313             :       NodeProperties::SetType(arguments_elements_state, Type::OtherInternal());
     314             :       ReplaceWithValue(node, arguments_elements_state);
     315             : 
     316        3709 :       for (Node* load : loads) {
     317        1083 :         switch (load->opcode()) {
     318             :           case IrOpcode::kLoadElement: {
     319         586 :             Node* index = NodeProperties::GetValueInput(load, 1);
     320             :             // {offset} is a reverted index starting from 1. The base address is
     321             :             // adapted to allow offsets starting from 1.
     322         586 :             Node* offset = jsgraph()->graph()->NewNode(
     323             :                 jsgraph()->simplified()->NumberSubtract(), arguments_length,
     324             :                 index);
     325             :             NodeProperties::SetType(offset,
     326         586 :                                     TypeCache::Get()->kArgumentsLengthType);
     327         586 :             NodeProperties::ReplaceValueInput(load, arguments_frame, 0);
     328         586 :             NodeProperties::ReplaceValueInput(load, offset, 1);
     329         586 :             NodeProperties::ChangeOp(
     330         586 :                 load, jsgraph()->simplified()->LoadStackArgument());
     331         586 :             break;
     332             :           }
     333             :           case IrOpcode::kLoadField: {
     334             :             DCHECK_EQ(FieldAccessOf(load->op()).offset,
     335             :                       FixedArray::kLengthOffset);
     336         497 :             Node* length = NodeProperties::GetValueInput(node, 1);
     337             :             ReplaceWithValue(load, length);
     338             :             break;
     339             :           }
     340             :           default:
     341           0 :             UNREACHABLE();
     342             :         }
     343             :       }
     344             :     }
     345             :   }
     346      465465 : }
     347             : 
     348           0 : Node* NodeHashCache::Query(Node* node) {
     349             :   auto it = cache_.find(node);
     350    19845137 :   if (it != cache_.end()) {
     351      231029 :     return *it;
     352             :   } else {
     353             :     return nullptr;
     354             :   }
     355             : }
     356             : 
     357      112782 : NodeHashCache::Constructor::Constructor(NodeHashCache* cache,
     358             :                                         const Operator* op, int input_count,
     359             :                                         Node** inputs, Type type)
     360      112782 :     : node_cache_(cache), from_(nullptr) {
     361      112782 :   if (node_cache_->temp_nodes_.size() > 0) {
     362       35889 :     tmp_ = node_cache_->temp_nodes_.back();
     363             :     node_cache_->temp_nodes_.pop_back();
     364       35889 :     int tmp_input_count = tmp_->InputCount();
     365       35889 :     if (input_count <= tmp_input_count) {
     366       21537 :       tmp_->TrimInputCount(input_count);
     367             :     }
     368      444763 :     for (int i = 0; i < input_count; ++i) {
     369      204437 :       if (i < tmp_input_count) {
     370      159419 :         tmp_->ReplaceInput(i, inputs[i]);
     371             :       } else {
     372       45018 :         tmp_->AppendInput(node_cache_->graph_->zone(), inputs[i]);
     373             :       }
     374             :     }
     375       35889 :     NodeProperties::ChangeOp(tmp_, op);
     376             :   } else {
     377       76893 :     tmp_ = node_cache_->graph_->NewNode(op, input_count, inputs);
     378             :   }
     379      112782 :   NodeProperties::SetType(tmp_, type);
     380      112782 : }
     381             : 
     382    19845275 : Node* NodeHashCache::Constructor::Get() {
     383             :   DCHECK(tmp_ || from_);
     384             :   Node* node;
     385    19845275 :   if (!tmp_) {
     386    19486870 :     node = node_cache_->Query(from_);
     387    19486732 :     if (!node) node = from_;
     388             :   } else {
     389      358405 :     node = node_cache_->Query(tmp_);
     390      358405 :     if (node) {
     391      228383 :       node_cache_->temp_nodes_.push_back(tmp_);
     392             :     } else {
     393      130022 :       node = tmp_;
     394      130022 :       node_cache_->Insert(node);
     395             :     }
     396             :   }
     397    19845137 :   tmp_ = from_ = nullptr;
     398    19845137 :   return node;
     399             : }
     400             : 
     401      965146 : Node* NodeHashCache::Constructor::MutableNode() {
     402             :   DCHECK(tmp_ || from_);
     403      965146 :   if (!tmp_) {
     404      245623 :     if (node_cache_->temp_nodes_.empty()) {
     405       56964 :       tmp_ = node_cache_->graph_->CloneNode(from_);
     406             :     } else {
     407      188659 :       tmp_ = node_cache_->temp_nodes_.back();
     408             :       node_cache_->temp_nodes_.pop_back();
     409      188659 :       int from_input_count = from_->InputCount();
     410      188659 :       int tmp_input_count = tmp_->InputCount();
     411      188659 :       if (from_input_count <= tmp_input_count) {
     412      144227 :         tmp_->TrimInputCount(from_input_count);
     413             :       }
     414     2169747 :       for (int i = 0; i < from_input_count; ++i) {
     415      990544 :         if (i < tmp_input_count) {
     416     1630508 :           tmp_->ReplaceInput(i, from_->InputAt(i));
     417             :         } else {
     418      350580 :           tmp_->AppendInput(node_cache_->graph_->zone(), from_->InputAt(i));
     419             :         }
     420             :       }
     421      188659 :       NodeProperties::SetType(tmp_, NodeProperties::GetType(from_));
     422      188659 :       NodeProperties::ChangeOp(tmp_, from_->op());
     423             :     }
     424             :   }
     425      965146 :   return tmp_;
     426             : }
     427             : 
     428             : #undef TRACE
     429             : 
     430             : }  // namespace compiler
     431             : }  // namespace internal
     432      122004 : }  // namespace v8

Generated by: LCOV version 1.10