js-context-specialization.cc 10.1 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 92 93 94 95 96 97 98 99 100 101 102 103 104
namespace {

bool IsContextParameter(Node* node) {
  DCHECK_EQ(IrOpcode::kParameter, node->opcode());
  Node* const start = NodeProperties::GetValueInput(node, 0);
  DCHECK_EQ(IrOpcode::kStart, start->opcode());
  int const index = ParameterIndexOf(node->op());
  // The context is always the last parameter to a JavaScript function, and
  // {Parameter} indices start at -1, so value outputs of {Start} look like
  // this: closure, receiver, param0, ..., paramN, context.
  return index == start->op()->ValueOutputCount() - 2;
}

// 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.
105
base::Optional<ContextRef> GetSpecializationContext(
106
    JSHeapBroker* broker, Node* node, size_t* distance,
107
    Maybe<OuterContext> maybe_outer) {
108
  switch (node->opcode()) {
109
    case IrOpcode::kHeapConstant: {
110
      HeapObjectRef object(broker, HeapConstantOf(node->op()));
111
      if (object.IsContext()) return object.AsContext();
112 113
      break;
    }
114 115 116 117 118
    case IrOpcode::kParameter: {
      OuterContext outer;
      if (maybe_outer.To(&outer) && IsContextParameter(node) &&
          *distance >= outer.distance) {
        *distance -= outer.distance;
119
        return ContextRef(broker, outer.context);
120 121 122 123 124 125
      }
      break;
    }
    default:
      break;
  }
126
  return base::Optional<ContextRef>();
127 128 129 130
}

}  // anonymous namespace

131
Reduction JSContextSpecialization::ReduceJSLoadContext(Node* node) {
132
  DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
133

134
  const ContextAccess& access = ContextAccessOf(node->op());
135 136 137
  size_t depth = access.depth();

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

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

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

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

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

  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()) {
174 175 176 177 178
    // 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.
179
    OddballType oddball_type = maybe_value->AsHeapObject().map().oddball_type();
180
    if (oddball_type == OddballType::kUndefined ||
181
        oddball_type == OddballType::kHole) {
182
      return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
183
    }
184 185 186
  }

  // Success. The context load can be replaced with the constant.
187
  Node* constant = jsgraph_->Constant(*maybe_value);
188
  ReplaceWithValue(node, constant);
189
  return Replace(constant);
190
}
191 192


193
Reduction JSContextSpecialization::ReduceJSStoreContext(Node* node) {
194 195
  DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());

196
  const ContextAccess& access = ContextAccessOf(node->op());
197 198 199 200
  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.
201
  Node* context = NodeProperties::GetOuterContext(node, &depth);
202

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

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

219
  return SimplifyJSStoreContext(node, jsgraph()->Constant(concrete), depth);
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 274 275 276 277
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();
  }
}
278

279 280 281
Isolate* JSContextSpecialization::isolate() const {
  return jsgraph()->isolate();
}
282

283 284 285
}  // namespace compiler
}  // namespace internal
}  // namespace v8