js-context-specialization.cc 9.79 KB
Newer Older
1 2 3 4
// Copyright 2014 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.

5 6
#include "src/compiler/js-context-specialization.h"

7
#include "src/compiler/common-operator.h"
8
#include "src/compiler/js-graph.h"
9
#include "src/compiler/js-heap-broker.h"
10
#include "src/compiler/js-operator.h"
11
#include "src/compiler/linkage.h"
12
#include "src/compiler/node-matchers.h"
13
#include "src/compiler/node-properties.h"
14
#include "src/objects/contexts-inl.h"
15 16 17 18 19

namespace v8 {
namespace internal {
namespace compiler {

20 21
Reduction JSContextSpecialization::Reduce(Node* node) {
  switch (node->opcode()) {
22 23
    case IrOpcode::kParameter:
      return ReduceParameter(node);
24 25 26 27
    case IrOpcode::kJSLoadContext:
      return ReduceJSLoadContext(node);
    case IrOpcode::kJSStoreContext:
      return ReduceJSStoreContext(node);
28 29
    case IrOpcode::kJSGetImportMeta:
      return ReduceJSGetImportMeta(node);
30 31
    default:
      break;
32
  }
33 34 35
  return NoChange();
}

36 37 38 39 40 41 42
Reduction JSContextSpecialization::ReduceParameter(Node* node) {
  DCHECK_EQ(IrOpcode::kParameter, node->opcode());
  int const index = ParameterIndexOf(node->op());
  if (index == Linkage::kJSCallClosureParamIndex) {
    // Constant-fold the function parameter {node}.
    Handle<JSFunction> function;
    if (closure().ToHandle(&function)) {
43
      Node* value = jsgraph()->Constant(JSFunctionRef(broker_, function));
44 45 46 47 48 49
      return Replace(value);
    }
  }
  return NoChange();
}

50 51 52 53 54 55 56 57 58 59 60
Reduction JSContextSpecialization::SimplifyJSLoadContext(Node* node,
                                                         Node* new_context,
                                                         size_t new_depth) {
  DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
  const ContextAccess& access = ContextAccessOf(node->op());
  DCHECK_LE(new_depth, access.depth());

  if (new_depth == access.depth() &&
      new_context == NodeProperties::GetContextInput(node)) {
    return NoChange();
  }
61

62 63 64 65 66
  const Operator* op = jsgraph_->javascript()->LoadContext(
      new_depth, access.index(), access.immutable());
  NodeProperties::ReplaceContextInput(node, new_context);
  NodeProperties::ChangeOp(node, op);
  return Changed(node);
67 68
}

69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
Reduction JSContextSpecialization::SimplifyJSStoreContext(Node* node,
                                                          Node* new_context,
                                                          size_t new_depth) {
  DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());
  const ContextAccess& access = ContextAccessOf(node->op());
  DCHECK_LE(new_depth, access.depth());

  if (new_depth == access.depth() &&
      new_context == NodeProperties::GetContextInput(node)) {
    return NoChange();
  }

  const Operator* op =
      jsgraph_->javascript()->StoreContext(new_depth, access.index());
  NodeProperties::ReplaceContextInput(node, new_context);
  NodeProperties::ChangeOp(node, op);
  return Changed(node);
}
87

88 89 90 91
namespace {

bool IsContextParameter(Node* node) {
  DCHECK_EQ(IrOpcode::kParameter, node->opcode());
92 93 94
  return ParameterIndexOf(node->op()) ==
         StartNode{NodeProperties::GetValueInput(node, 0)}
             .ContextParameterIndex_MaybeNonStandardLayout();
95 96 97 98 99 100
}

// Given a context {node} and the {distance} from that context to the target
// context (which we want to read from or store to), try to return a
// specialization context.  If successful, update {distance} to whatever
// distance remains from the specialization context.
101
base::Optional<ContextRef> GetSpecializationContext(
102
    JSHeapBroker* broker, Node* node, size_t* distance,
103
    Maybe<OuterContext> maybe_outer) {
104
  switch (node->opcode()) {
105
    case IrOpcode::kHeapConstant: {
106
      HeapObjectRef object(broker, HeapConstantOf(node->op()));
107
      if (object.IsContext()) return object.AsContext();
108 109
      break;
    }
110 111 112 113 114
    case IrOpcode::kParameter: {
      OuterContext outer;
      if (maybe_outer.To(&outer) && IsContextParameter(node) &&
          *distance >= outer.distance) {
        *distance -= outer.distance;
115
        return ContextRef(broker, outer.context);
116 117 118 119 120 121
      }
      break;
    }
    default:
      break;
  }
122
  return base::Optional<ContextRef>();
123 124 125 126
}

}  // anonymous namespace

127
Reduction JSContextSpecialization::ReduceJSLoadContext(Node* node) {
128
  DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
129

130
  const ContextAccess& access = ContextAccessOf(node->op());
131 132 133
  size_t depth = access.depth();

  // First walk up the context chain in the graph as far as possible.
134
  Node* context = NodeProperties::GetOuterContext(node, &depth);
135

136
  base::Optional<ContextRef> maybe_concrete =
137
      GetSpecializationContext(broker(), context, &depth, outer());
138
  if (!maybe_concrete.has_value()) {
139 140
    // We do not have a concrete context object, so we can only partially reduce
    // the load by folding-in the outer context node.
141
    return SimplifyJSLoadContext(node, context, depth);
142 143 144
  }

  // Now walk up the concrete context chain for the remaining depth.
145
  ContextRef concrete = maybe_concrete.value();
146 147 148 149
  concrete = concrete.previous(&depth);
  if (depth > 0) {
    TRACE_BROKER_MISSING(broker(), "previous value for context " << concrete);
    return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
150 151 152
  }

  if (!access.immutable()) {
153 154
    // We found the requested context object but since the context slot is
    // mutable we can only partially reduce the load.
155
    return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
156 157 158
  }

  // This will hold the final value, if we can figure it out.
159
  base::Optional<ObjectRef> maybe_value;
160
  maybe_value = concrete.get(static_cast<int>(access.index()));
161 162 163 164 165 166 167 168 169

  if (!maybe_value.has_value()) {
    TRACE_BROKER_MISSING(broker(), "slot value " << access.index()
                                                 << " for context "
                                                 << concrete);
    return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
  }

  if (!maybe_value->IsSmi()) {
170 171 172 173 174
    // Even though the context slot is immutable, the context might have escaped
    // before the function to which it belongs has initialized the slot.
    // We must be conservative and check if the value in the slot is currently
    // the hole or undefined. Only if it is neither of these, can we be sure
    // that it won't change anymore.
175
    OddballType oddball_type = maybe_value->AsHeapObject().map().oddball_type();
176
    if (oddball_type == OddballType::kUndefined ||
177
        oddball_type == OddballType::kHole) {
178
      return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
179
    }
180 181 182
  }

  // Success. The context load can be replaced with the constant.
183
  Node* constant = jsgraph_->Constant(*maybe_value);
184
  ReplaceWithValue(node, constant);
185
  return Replace(constant);
186
}
187 188


189
Reduction JSContextSpecialization::ReduceJSStoreContext(Node* node) {
190 191
  DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());

192
  const ContextAccess& access = ContextAccessOf(node->op());
193 194 195 196
  size_t depth = access.depth();

  // First walk up the context chain in the graph until we reduce the depth to 0
  // or hit a node that does not have a CreateXYZContext operator.
197
  Node* context = NodeProperties::GetOuterContext(node, &depth);
198

199
  base::Optional<ContextRef> maybe_concrete =
200
      GetSpecializationContext(broker(), context, &depth, outer());
201
  if (!maybe_concrete.has_value()) {
202 203
    // We do not have a concrete context object, so we can only partially reduce
    // the load by folding-in the outer context node.
204
    return SimplifyJSStoreContext(node, context, depth);
205 206
  }

207
  // Now walk up the concrete context chain for the remaining depth.
208
  ContextRef concrete = maybe_concrete.value();
209 210 211 212
  concrete = concrete.previous(&depth);
  if (depth > 0) {
    TRACE_BROKER_MISSING(broker(), "previous value for context " << concrete);
    return SimplifyJSStoreContext(node, jsgraph()->Constant(concrete), depth);
213 214
  }

215
  return SimplifyJSStoreContext(node, jsgraph()->Constant(concrete), depth);
216
}
217

218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
base::Optional<ContextRef> GetModuleContext(JSHeapBroker* broker, Node* node,
                                            Maybe<OuterContext> maybe_context) {
  size_t depth = std::numeric_limits<size_t>::max();
  Node* context = NodeProperties::GetOuterContext(node, &depth);

  auto find_context = [](ContextRef c) {
    while (c.map().instance_type() != MODULE_CONTEXT_TYPE) {
      size_t depth = 1;
      c = c.previous(&depth);
      CHECK_EQ(depth, 0);
    }
    return c;
  };

  switch (context->opcode()) {
    case IrOpcode::kHeapConstant: {
      HeapObjectRef object(broker, HeapConstantOf(context->op()));
      if (object.IsContext()) {
        return find_context(object.AsContext());
      }
      break;
    }
    case IrOpcode::kParameter: {
      OuterContext outer;
      if (maybe_context.To(&outer) && IsContextParameter(context)) {
        return find_context(ContextRef(broker, outer.context));
      }
      break;
    }
    default:
      break;
  }

  return base::Optional<ContextRef>();
}

Reduction JSContextSpecialization::ReduceJSGetImportMeta(Node* node) {
  base::Optional<ContextRef> maybe_context =
      GetModuleContext(broker(), node, outer());
  if (!maybe_context.has_value()) return NoChange();

  ContextRef context = maybe_context.value();
  SourceTextModuleRef module =
      context.get(Context::EXTENSION_INDEX).value().AsSourceTextModule();
  ObjectRef import_meta = module.import_meta();
  if (import_meta.IsJSObject()) {
    Node* import_meta_const = jsgraph()->Constant(import_meta);
    ReplaceWithValue(node, import_meta_const);
    return Changed(import_meta_const);
  } else {
    DCHECK(import_meta.IsTheHole());
    // The import.meta object has not yet been created. Let JSGenericLowering
    // replace the operator with a runtime call.
    return NoChange();
  }
}
274

275 276 277
Isolate* JSContextSpecialization::isolate() const {
  return jsgraph()->isolate();
}
278

279 280 281
}  // namespace compiler
}  // namespace internal
}  // namespace v8