Home | History | Annotate | Download | only in compiler
      1 // Copyright 2014 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/js-context-specialization.h"
      6 
      7 #include "src/compiler/common-operator.h"
      8 #include "src/compiler/js-graph.h"
      9 #include "src/compiler/js-operator.h"
     10 #include "src/compiler/linkage.h"
     11 #include "src/compiler/node-matchers.h"
     12 #include "src/compiler/node-properties.h"
     13 #include "src/contexts-inl.h"
     14 
     15 namespace v8 {
     16 namespace internal {
     17 namespace compiler {
     18 
     19 Reduction JSContextSpecialization::Reduce(Node* node) {
     20   switch (node->opcode()) {
     21     case IrOpcode::kParameter:
     22       return ReduceParameter(node);
     23     case IrOpcode::kJSLoadContext:
     24       return ReduceJSLoadContext(node);
     25     case IrOpcode::kJSStoreContext:
     26       return ReduceJSStoreContext(node);
     27     default:
     28       break;
     29   }
     30   return NoChange();
     31 }
     32 
     33 Reduction JSContextSpecialization::ReduceParameter(Node* node) {
     34   DCHECK_EQ(IrOpcode::kParameter, node->opcode());
     35   int const index = ParameterIndexOf(node->op());
     36   if (index == Linkage::kJSCallClosureParamIndex) {
     37     // Constant-fold the function parameter {node}.
     38     Handle<JSFunction> function;
     39     if (closure().ToHandle(&function)) {
     40       Node* value = jsgraph()->HeapConstant(function);
     41       return Replace(value);
     42     }
     43   }
     44   return NoChange();
     45 }
     46 
     47 Reduction JSContextSpecialization::SimplifyJSLoadContext(Node* node,
     48                                                          Node* new_context,
     49                                                          size_t new_depth) {
     50   DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
     51   const ContextAccess& access = ContextAccessOf(node->op());
     52   DCHECK_LE(new_depth, access.depth());
     53 
     54   if (new_depth == access.depth() &&
     55       new_context == NodeProperties::GetContextInput(node)) {
     56     return NoChange();
     57   }
     58 
     59   const Operator* op = jsgraph_->javascript()->LoadContext(
     60       new_depth, access.index(), access.immutable());
     61   NodeProperties::ReplaceContextInput(node, new_context);
     62   NodeProperties::ChangeOp(node, op);
     63   return Changed(node);
     64 }
     65 
     66 Reduction JSContextSpecialization::SimplifyJSStoreContext(Node* node,
     67                                                           Node* new_context,
     68                                                           size_t new_depth) {
     69   DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());
     70   const ContextAccess& access = ContextAccessOf(node->op());
     71   DCHECK_LE(new_depth, access.depth());
     72 
     73   if (new_depth == access.depth() &&
     74       new_context == NodeProperties::GetContextInput(node)) {
     75     return NoChange();
     76   }
     77 
     78   const Operator* op =
     79       jsgraph_->javascript()->StoreContext(new_depth, access.index());
     80   NodeProperties::ReplaceContextInput(node, new_context);
     81   NodeProperties::ChangeOp(node, op);
     82   return Changed(node);
     83 }
     84 
     85 namespace {
     86 
     87 bool IsContextParameter(Node* node) {
     88   DCHECK_EQ(IrOpcode::kParameter, node->opcode());
     89   Node* const start = NodeProperties::GetValueInput(node, 0);
     90   DCHECK_EQ(IrOpcode::kStart, start->opcode());
     91   int const index = ParameterIndexOf(node->op());
     92   // The context is always the last parameter to a JavaScript function, and
     93   // {Parameter} indices start at -1, so value outputs of {Start} look like
     94   // this: closure, receiver, param0, ..., paramN, context.
     95   return index == start->op()->ValueOutputCount() - 2;
     96 }
     97 
     98 // Given a context {node} and the {distance} from that context to the target
     99 // context (which we want to read from or store to), try to return a
    100 // specialization context.  If successful, update {distance} to whatever
    101 // distance remains from the specialization context.
    102 base::Optional<ContextRef> GetSpecializationContext(
    103     JSHeapBroker* broker, Node* node, size_t* distance,
    104     Maybe<OuterContext> maybe_outer) {
    105   switch (node->opcode()) {
    106     case IrOpcode::kHeapConstant: {
    107       HeapObjectRef object(broker, HeapConstantOf(node->op()));
    108       if (object.IsContext()) return object.AsContext();
    109       break;
    110     }
    111     case IrOpcode::kParameter: {
    112       OuterContext outer;
    113       if (maybe_outer.To(&outer) && IsContextParameter(node) &&
    114           *distance >= outer.distance) {
    115         *distance -= outer.distance;
    116         return ContextRef(broker, outer.context);
    117       }
    118       break;
    119     }
    120     default:
    121       break;
    122   }
    123   return base::Optional<ContextRef>();
    124 }
    125 
    126 }  // anonymous namespace
    127 
    128 Reduction JSContextSpecialization::ReduceJSLoadContext(Node* node) {
    129   DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
    130 
    131   const ContextAccess& access = ContextAccessOf(node->op());
    132   size_t depth = access.depth();
    133 
    134   // First walk up the context chain in the graph as far as possible.
    135   Node* context = NodeProperties::GetOuterContext(node, &depth);
    136 
    137   base::Optional<ContextRef> maybe_concrete =
    138       GetSpecializationContext(js_heap_broker(), context, &depth, outer());
    139   if (!maybe_concrete.has_value()) {
    140     // We do not have a concrete context object, so we can only partially reduce
    141     // the load by folding-in the outer context node.
    142     return SimplifyJSLoadContext(node, context, depth);
    143   }
    144 
    145   // Now walk up the concrete context chain for the remaining depth.
    146   ContextRef concrete = maybe_concrete.value();
    147   for (; depth > 0; --depth) {
    148     concrete = concrete.previous().value();
    149   }
    150 
    151   if (!access.immutable()) {
    152     // We found the requested context object but since the context slot is
    153     // mutable we can only partially reduce the load.
    154     return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
    155   }
    156 
    157   // This will hold the final value, if we can figure it out.
    158   base::Optional<ObjectRef> maybe_value;
    159 
    160   maybe_value = concrete.get(static_cast<int>(access.index()));
    161   if (maybe_value.has_value() && !maybe_value->IsSmi()) {
    162     // Even though the context slot is immutable, the context might have escaped
    163     // before the function to which it belongs has initialized the slot.
    164     // We must be conservative and check if the value in the slot is currently
    165     // the hole or undefined. Only if it is neither of these, can we be sure
    166     // that it won't change anymore.
    167     OddballType oddball_type = maybe_value->oddball_type();
    168     if (oddball_type == OddballType::kUndefined ||
    169         oddball_type == OddballType::kHole) {
    170       maybe_value.reset();
    171     }
    172   }
    173 
    174   if (!maybe_value.has_value()) {
    175     return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
    176   }
    177 
    178   // Success. The context load can be replaced with the constant.
    179   // TODO(titzer): record the specialization for sharing code across
    180   // multiple contexts that have the same value in the corresponding context
    181   // slot.
    182   Node* constant = jsgraph_->Constant(*maybe_value);
    183   ReplaceWithValue(node, constant);
    184   return Replace(constant);
    185 }
    186 
    187 
    188 Reduction JSContextSpecialization::ReduceJSStoreContext(Node* node) {
    189   DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());
    190 
    191   const ContextAccess& access = ContextAccessOf(node->op());
    192   size_t depth = access.depth();
    193 
    194   // First walk up the context chain in the graph until we reduce the depth to 0
    195   // or hit a node that does not have a CreateXYZContext operator.
    196   Node* context = NodeProperties::GetOuterContext(node, &depth);
    197 
    198   base::Optional<ContextRef> maybe_concrete =
    199       GetSpecializationContext(js_heap_broker(), context, &depth, outer());
    200   if (!maybe_concrete.has_value()) {
    201     // We do not have a concrete context object, so we can only partially reduce
    202     // the load by folding-in the outer context node.
    203     return SimplifyJSStoreContext(node, context, depth);
    204   }
    205 
    206   // Now walk up the concrete context chain for the remaining depth.
    207   ContextRef concrete = maybe_concrete.value();
    208   for (; depth > 0; --depth) {
    209     concrete = concrete.previous().value();
    210   }
    211 
    212   return SimplifyJSStoreContext(node, jsgraph()->Constant(concrete), depth);
    213 }
    214 
    215 
    216 Isolate* JSContextSpecialization::isolate() const {
    217   return jsgraph()->isolate();
    218 }
    219 
    220 }  // namespace compiler
    221 }  // namespace internal
    222 }  // namespace v8
    223