11cb0ef41Sopenharmony_ci// Copyright 2014 the V8 project authors. All rights reserved.
21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
31cb0ef41Sopenharmony_ci// found in the LICENSE file.
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ci#include "src/compiler/js-context-specialization.h"
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_ci#include "src/compiler/common-operator.h"
81cb0ef41Sopenharmony_ci#include "src/compiler/js-graph.h"
91cb0ef41Sopenharmony_ci#include "src/compiler/js-heap-broker.h"
101cb0ef41Sopenharmony_ci#include "src/compiler/js-operator.h"
111cb0ef41Sopenharmony_ci#include "src/compiler/linkage.h"
121cb0ef41Sopenharmony_ci#include "src/compiler/node-matchers.h"
131cb0ef41Sopenharmony_ci#include "src/compiler/node-properties.h"
141cb0ef41Sopenharmony_ci#include "src/objects/contexts-inl.h"
151cb0ef41Sopenharmony_ci
161cb0ef41Sopenharmony_cinamespace v8 {
171cb0ef41Sopenharmony_cinamespace internal {
181cb0ef41Sopenharmony_cinamespace compiler {
191cb0ef41Sopenharmony_ci
201cb0ef41Sopenharmony_ciReduction JSContextSpecialization::Reduce(Node* node) {
211cb0ef41Sopenharmony_ci  switch (node->opcode()) {
221cb0ef41Sopenharmony_ci    case IrOpcode::kParameter:
231cb0ef41Sopenharmony_ci      return ReduceParameter(node);
241cb0ef41Sopenharmony_ci    case IrOpcode::kJSLoadContext:
251cb0ef41Sopenharmony_ci      return ReduceJSLoadContext(node);
261cb0ef41Sopenharmony_ci    case IrOpcode::kJSStoreContext:
271cb0ef41Sopenharmony_ci      return ReduceJSStoreContext(node);
281cb0ef41Sopenharmony_ci    case IrOpcode::kJSGetImportMeta:
291cb0ef41Sopenharmony_ci      return ReduceJSGetImportMeta(node);
301cb0ef41Sopenharmony_ci    default:
311cb0ef41Sopenharmony_ci      break;
321cb0ef41Sopenharmony_ci  }
331cb0ef41Sopenharmony_ci  return NoChange();
341cb0ef41Sopenharmony_ci}
351cb0ef41Sopenharmony_ci
361cb0ef41Sopenharmony_ciReduction JSContextSpecialization::ReduceParameter(Node* node) {
371cb0ef41Sopenharmony_ci  DCHECK_EQ(IrOpcode::kParameter, node->opcode());
381cb0ef41Sopenharmony_ci  int const index = ParameterIndexOf(node->op());
391cb0ef41Sopenharmony_ci  if (index == Linkage::kJSCallClosureParamIndex) {
401cb0ef41Sopenharmony_ci    // Constant-fold the function parameter {node}.
411cb0ef41Sopenharmony_ci    Handle<JSFunction> function;
421cb0ef41Sopenharmony_ci    if (closure().ToHandle(&function)) {
431cb0ef41Sopenharmony_ci      Node* value = jsgraph()->Constant(MakeRef(broker_, function));
441cb0ef41Sopenharmony_ci      return Replace(value);
451cb0ef41Sopenharmony_ci    }
461cb0ef41Sopenharmony_ci  }
471cb0ef41Sopenharmony_ci  return NoChange();
481cb0ef41Sopenharmony_ci}
491cb0ef41Sopenharmony_ci
501cb0ef41Sopenharmony_ciReduction JSContextSpecialization::SimplifyJSLoadContext(Node* node,
511cb0ef41Sopenharmony_ci                                                         Node* new_context,
521cb0ef41Sopenharmony_ci                                                         size_t new_depth) {
531cb0ef41Sopenharmony_ci  DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
541cb0ef41Sopenharmony_ci  const ContextAccess& access = ContextAccessOf(node->op());
551cb0ef41Sopenharmony_ci  DCHECK_LE(new_depth, access.depth());
561cb0ef41Sopenharmony_ci
571cb0ef41Sopenharmony_ci  if (new_depth == access.depth() &&
581cb0ef41Sopenharmony_ci      new_context == NodeProperties::GetContextInput(node)) {
591cb0ef41Sopenharmony_ci    return NoChange();
601cb0ef41Sopenharmony_ci  }
611cb0ef41Sopenharmony_ci
621cb0ef41Sopenharmony_ci  const Operator* op = jsgraph_->javascript()->LoadContext(
631cb0ef41Sopenharmony_ci      new_depth, access.index(), access.immutable());
641cb0ef41Sopenharmony_ci  NodeProperties::ReplaceContextInput(node, new_context);
651cb0ef41Sopenharmony_ci  NodeProperties::ChangeOp(node, op);
661cb0ef41Sopenharmony_ci  return Changed(node);
671cb0ef41Sopenharmony_ci}
681cb0ef41Sopenharmony_ci
691cb0ef41Sopenharmony_ciReduction JSContextSpecialization::SimplifyJSStoreContext(Node* node,
701cb0ef41Sopenharmony_ci                                                          Node* new_context,
711cb0ef41Sopenharmony_ci                                                          size_t new_depth) {
721cb0ef41Sopenharmony_ci  DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());
731cb0ef41Sopenharmony_ci  const ContextAccess& access = ContextAccessOf(node->op());
741cb0ef41Sopenharmony_ci  DCHECK_LE(new_depth, access.depth());
751cb0ef41Sopenharmony_ci
761cb0ef41Sopenharmony_ci  if (new_depth == access.depth() &&
771cb0ef41Sopenharmony_ci      new_context == NodeProperties::GetContextInput(node)) {
781cb0ef41Sopenharmony_ci    return NoChange();
791cb0ef41Sopenharmony_ci  }
801cb0ef41Sopenharmony_ci
811cb0ef41Sopenharmony_ci  const Operator* op =
821cb0ef41Sopenharmony_ci      jsgraph_->javascript()->StoreContext(new_depth, access.index());
831cb0ef41Sopenharmony_ci  NodeProperties::ReplaceContextInput(node, new_context);
841cb0ef41Sopenharmony_ci  NodeProperties::ChangeOp(node, op);
851cb0ef41Sopenharmony_ci  return Changed(node);
861cb0ef41Sopenharmony_ci}
871cb0ef41Sopenharmony_ci
881cb0ef41Sopenharmony_cinamespace {
891cb0ef41Sopenharmony_ci
901cb0ef41Sopenharmony_cibool IsContextParameter(Node* node) {
911cb0ef41Sopenharmony_ci  DCHECK_EQ(IrOpcode::kParameter, node->opcode());
921cb0ef41Sopenharmony_ci  return ParameterIndexOf(node->op()) ==
931cb0ef41Sopenharmony_ci         StartNode{NodeProperties::GetValueInput(node, 0)}
941cb0ef41Sopenharmony_ci             .ContextParameterIndex_MaybeNonStandardLayout();
951cb0ef41Sopenharmony_ci}
961cb0ef41Sopenharmony_ci
971cb0ef41Sopenharmony_ci// Given a context {node} and the {distance} from that context to the target
981cb0ef41Sopenharmony_ci// context (which we want to read from or store to), try to return a
991cb0ef41Sopenharmony_ci// specialization context.  If successful, update {distance} to whatever
1001cb0ef41Sopenharmony_ci// distance remains from the specialization context.
1011cb0ef41Sopenharmony_cibase::Optional<ContextRef> GetSpecializationContext(
1021cb0ef41Sopenharmony_ci    JSHeapBroker* broker, Node* node, size_t* distance,
1031cb0ef41Sopenharmony_ci    Maybe<OuterContext> maybe_outer) {
1041cb0ef41Sopenharmony_ci  switch (node->opcode()) {
1051cb0ef41Sopenharmony_ci    case IrOpcode::kHeapConstant: {
1061cb0ef41Sopenharmony_ci      // TODO(jgruber,chromium:1209798): Using kAssumeMemoryFence works around
1071cb0ef41Sopenharmony_ci      // the fact that the graph stores handles (and not refs). The assumption
1081cb0ef41Sopenharmony_ci      // is that any handle inserted into the graph is safe to read; but we
1091cb0ef41Sopenharmony_ci      // don't preserve the reason why it is safe to read. Thus we must
1101cb0ef41Sopenharmony_ci      // over-approximate here and assume the existence of a memory fence. In
1111cb0ef41Sopenharmony_ci      // the future, we should consider having the graph store ObjectRefs or
1121cb0ef41Sopenharmony_ci      // ObjectData pointer instead, which would make new ref construction here
1131cb0ef41Sopenharmony_ci      // unnecessary.
1141cb0ef41Sopenharmony_ci      HeapObjectRef object =
1151cb0ef41Sopenharmony_ci          MakeRefAssumeMemoryFence(broker, HeapConstantOf(node->op()));
1161cb0ef41Sopenharmony_ci      if (object.IsContext()) return object.AsContext();
1171cb0ef41Sopenharmony_ci      break;
1181cb0ef41Sopenharmony_ci    }
1191cb0ef41Sopenharmony_ci    case IrOpcode::kParameter: {
1201cb0ef41Sopenharmony_ci      OuterContext outer;
1211cb0ef41Sopenharmony_ci      if (maybe_outer.To(&outer) && IsContextParameter(node) &&
1221cb0ef41Sopenharmony_ci          *distance >= outer.distance) {
1231cb0ef41Sopenharmony_ci        *distance -= outer.distance;
1241cb0ef41Sopenharmony_ci        return MakeRef(broker, outer.context);
1251cb0ef41Sopenharmony_ci      }
1261cb0ef41Sopenharmony_ci      break;
1271cb0ef41Sopenharmony_ci    }
1281cb0ef41Sopenharmony_ci    default:
1291cb0ef41Sopenharmony_ci      break;
1301cb0ef41Sopenharmony_ci  }
1311cb0ef41Sopenharmony_ci  return base::Optional<ContextRef>();
1321cb0ef41Sopenharmony_ci}
1331cb0ef41Sopenharmony_ci
1341cb0ef41Sopenharmony_ci}  // anonymous namespace
1351cb0ef41Sopenharmony_ci
1361cb0ef41Sopenharmony_ciReduction JSContextSpecialization::ReduceJSLoadContext(Node* node) {
1371cb0ef41Sopenharmony_ci  DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
1381cb0ef41Sopenharmony_ci
1391cb0ef41Sopenharmony_ci  const ContextAccess& access = ContextAccessOf(node->op());
1401cb0ef41Sopenharmony_ci  size_t depth = access.depth();
1411cb0ef41Sopenharmony_ci
1421cb0ef41Sopenharmony_ci  // First walk up the context chain in the graph as far as possible.
1431cb0ef41Sopenharmony_ci  Node* context = NodeProperties::GetOuterContext(node, &depth);
1441cb0ef41Sopenharmony_ci
1451cb0ef41Sopenharmony_ci  base::Optional<ContextRef> maybe_concrete =
1461cb0ef41Sopenharmony_ci      GetSpecializationContext(broker(), context, &depth, outer());
1471cb0ef41Sopenharmony_ci  if (!maybe_concrete.has_value()) {
1481cb0ef41Sopenharmony_ci    // We do not have a concrete context object, so we can only partially reduce
1491cb0ef41Sopenharmony_ci    // the load by folding-in the outer context node.
1501cb0ef41Sopenharmony_ci    return SimplifyJSLoadContext(node, context, depth);
1511cb0ef41Sopenharmony_ci  }
1521cb0ef41Sopenharmony_ci
1531cb0ef41Sopenharmony_ci  // Now walk up the concrete context chain for the remaining depth.
1541cb0ef41Sopenharmony_ci  ContextRef concrete = maybe_concrete.value();
1551cb0ef41Sopenharmony_ci  concrete = concrete.previous(&depth);
1561cb0ef41Sopenharmony_ci  if (depth > 0) {
1571cb0ef41Sopenharmony_ci    TRACE_BROKER_MISSING(broker(), "previous value for context " << concrete);
1581cb0ef41Sopenharmony_ci    return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
1591cb0ef41Sopenharmony_ci  }
1601cb0ef41Sopenharmony_ci
1611cb0ef41Sopenharmony_ci  if (!access.immutable()) {
1621cb0ef41Sopenharmony_ci    // We found the requested context object but since the context slot is
1631cb0ef41Sopenharmony_ci    // mutable we can only partially reduce the load.
1641cb0ef41Sopenharmony_ci    return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
1651cb0ef41Sopenharmony_ci  }
1661cb0ef41Sopenharmony_ci
1671cb0ef41Sopenharmony_ci  // This will hold the final value, if we can figure it out.
1681cb0ef41Sopenharmony_ci  base::Optional<ObjectRef> maybe_value;
1691cb0ef41Sopenharmony_ci  maybe_value = concrete.get(static_cast<int>(access.index()));
1701cb0ef41Sopenharmony_ci
1711cb0ef41Sopenharmony_ci  if (!maybe_value.has_value()) {
1721cb0ef41Sopenharmony_ci    TRACE_BROKER_MISSING(broker(), "slot value " << access.index()
1731cb0ef41Sopenharmony_ci                                                 << " for context "
1741cb0ef41Sopenharmony_ci                                                 << concrete);
1751cb0ef41Sopenharmony_ci    return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
1761cb0ef41Sopenharmony_ci  }
1771cb0ef41Sopenharmony_ci
1781cb0ef41Sopenharmony_ci  if (!maybe_value->IsSmi()) {
1791cb0ef41Sopenharmony_ci    // Even though the context slot is immutable, the context might have escaped
1801cb0ef41Sopenharmony_ci    // before the function to which it belongs has initialized the slot.
1811cb0ef41Sopenharmony_ci    // We must be conservative and check if the value in the slot is currently
1821cb0ef41Sopenharmony_ci    // the hole or undefined. Only if it is neither of these, can we be sure
1831cb0ef41Sopenharmony_ci    // that it won't change anymore.
1841cb0ef41Sopenharmony_ci    OddballType oddball_type = maybe_value->AsHeapObject().map().oddball_type();
1851cb0ef41Sopenharmony_ci    if (oddball_type == OddballType::kUndefined ||
1861cb0ef41Sopenharmony_ci        oddball_type == OddballType::kHole) {
1871cb0ef41Sopenharmony_ci      return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
1881cb0ef41Sopenharmony_ci    }
1891cb0ef41Sopenharmony_ci  }
1901cb0ef41Sopenharmony_ci
1911cb0ef41Sopenharmony_ci  // Success. The context load can be replaced with the constant.
1921cb0ef41Sopenharmony_ci  Node* constant = jsgraph_->Constant(*maybe_value);
1931cb0ef41Sopenharmony_ci  ReplaceWithValue(node, constant);
1941cb0ef41Sopenharmony_ci  return Replace(constant);
1951cb0ef41Sopenharmony_ci}
1961cb0ef41Sopenharmony_ci
1971cb0ef41Sopenharmony_ci
1981cb0ef41Sopenharmony_ciReduction JSContextSpecialization::ReduceJSStoreContext(Node* node) {
1991cb0ef41Sopenharmony_ci  DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());
2001cb0ef41Sopenharmony_ci
2011cb0ef41Sopenharmony_ci  const ContextAccess& access = ContextAccessOf(node->op());
2021cb0ef41Sopenharmony_ci  size_t depth = access.depth();
2031cb0ef41Sopenharmony_ci
2041cb0ef41Sopenharmony_ci  // First walk up the context chain in the graph until we reduce the depth to 0
2051cb0ef41Sopenharmony_ci  // or hit a node that does not have a CreateXYZContext operator.
2061cb0ef41Sopenharmony_ci  Node* context = NodeProperties::GetOuterContext(node, &depth);
2071cb0ef41Sopenharmony_ci
2081cb0ef41Sopenharmony_ci  base::Optional<ContextRef> maybe_concrete =
2091cb0ef41Sopenharmony_ci      GetSpecializationContext(broker(), context, &depth, outer());
2101cb0ef41Sopenharmony_ci  if (!maybe_concrete.has_value()) {
2111cb0ef41Sopenharmony_ci    // We do not have a concrete context object, so we can only partially reduce
2121cb0ef41Sopenharmony_ci    // the load by folding-in the outer context node.
2131cb0ef41Sopenharmony_ci    return SimplifyJSStoreContext(node, context, depth);
2141cb0ef41Sopenharmony_ci  }
2151cb0ef41Sopenharmony_ci
2161cb0ef41Sopenharmony_ci  // Now walk up the concrete context chain for the remaining depth.
2171cb0ef41Sopenharmony_ci  ContextRef concrete = maybe_concrete.value();
2181cb0ef41Sopenharmony_ci  concrete = concrete.previous(&depth);
2191cb0ef41Sopenharmony_ci  if (depth > 0) {
2201cb0ef41Sopenharmony_ci    TRACE_BROKER_MISSING(broker(), "previous value for context " << concrete);
2211cb0ef41Sopenharmony_ci    return SimplifyJSStoreContext(node, jsgraph()->Constant(concrete), depth);
2221cb0ef41Sopenharmony_ci  }
2231cb0ef41Sopenharmony_ci
2241cb0ef41Sopenharmony_ci  return SimplifyJSStoreContext(node, jsgraph()->Constant(concrete), depth);
2251cb0ef41Sopenharmony_ci}
2261cb0ef41Sopenharmony_ci
2271cb0ef41Sopenharmony_cibase::Optional<ContextRef> GetModuleContext(JSHeapBroker* broker, Node* node,
2281cb0ef41Sopenharmony_ci                                            Maybe<OuterContext> maybe_context) {
2291cb0ef41Sopenharmony_ci  size_t depth = std::numeric_limits<size_t>::max();
2301cb0ef41Sopenharmony_ci  Node* context = NodeProperties::GetOuterContext(node, &depth);
2311cb0ef41Sopenharmony_ci
2321cb0ef41Sopenharmony_ci  auto find_context = [](ContextRef c) {
2331cb0ef41Sopenharmony_ci    while (c.map().instance_type() != MODULE_CONTEXT_TYPE) {
2341cb0ef41Sopenharmony_ci      size_t depth = 1;
2351cb0ef41Sopenharmony_ci      c = c.previous(&depth);
2361cb0ef41Sopenharmony_ci      CHECK_EQ(depth, 0);
2371cb0ef41Sopenharmony_ci    }
2381cb0ef41Sopenharmony_ci    return c;
2391cb0ef41Sopenharmony_ci  };
2401cb0ef41Sopenharmony_ci
2411cb0ef41Sopenharmony_ci  switch (context->opcode()) {
2421cb0ef41Sopenharmony_ci    case IrOpcode::kHeapConstant: {
2431cb0ef41Sopenharmony_ci      // TODO(jgruber,chromium:1209798): Using kAssumeMemoryFence works around
2441cb0ef41Sopenharmony_ci      // the fact that the graph stores handles (and not refs). The assumption
2451cb0ef41Sopenharmony_ci      // is that any handle inserted into the graph is safe to read; but we
2461cb0ef41Sopenharmony_ci      // don't preserve the reason why it is safe to read. Thus we must
2471cb0ef41Sopenharmony_ci      // over-approximate here and assume the existence of a memory fence. In
2481cb0ef41Sopenharmony_ci      // the future, we should consider having the graph store ObjectRefs or
2491cb0ef41Sopenharmony_ci      // ObjectData pointer instead, which would make new ref construction here
2501cb0ef41Sopenharmony_ci      // unnecessary.
2511cb0ef41Sopenharmony_ci      HeapObjectRef object =
2521cb0ef41Sopenharmony_ci          MakeRefAssumeMemoryFence(broker, HeapConstantOf(context->op()));
2531cb0ef41Sopenharmony_ci      if (object.IsContext()) {
2541cb0ef41Sopenharmony_ci        return find_context(object.AsContext());
2551cb0ef41Sopenharmony_ci      }
2561cb0ef41Sopenharmony_ci      break;
2571cb0ef41Sopenharmony_ci    }
2581cb0ef41Sopenharmony_ci    case IrOpcode::kParameter: {
2591cb0ef41Sopenharmony_ci      OuterContext outer;
2601cb0ef41Sopenharmony_ci      if (maybe_context.To(&outer) && IsContextParameter(context)) {
2611cb0ef41Sopenharmony_ci        return find_context(MakeRef(broker, outer.context));
2621cb0ef41Sopenharmony_ci      }
2631cb0ef41Sopenharmony_ci      break;
2641cb0ef41Sopenharmony_ci    }
2651cb0ef41Sopenharmony_ci    default:
2661cb0ef41Sopenharmony_ci      break;
2671cb0ef41Sopenharmony_ci  }
2681cb0ef41Sopenharmony_ci
2691cb0ef41Sopenharmony_ci  return base::Optional<ContextRef>();
2701cb0ef41Sopenharmony_ci}
2711cb0ef41Sopenharmony_ci
2721cb0ef41Sopenharmony_ciReduction JSContextSpecialization::ReduceJSGetImportMeta(Node* node) {
2731cb0ef41Sopenharmony_ci  base::Optional<ContextRef> maybe_context =
2741cb0ef41Sopenharmony_ci      GetModuleContext(broker(), node, outer());
2751cb0ef41Sopenharmony_ci  if (!maybe_context.has_value()) return NoChange();
2761cb0ef41Sopenharmony_ci
2771cb0ef41Sopenharmony_ci  ContextRef context = maybe_context.value();
2781cb0ef41Sopenharmony_ci  base::Optional<ObjectRef> module = context.get(Context::EXTENSION_INDEX);
2791cb0ef41Sopenharmony_ci  if (!module.has_value()) return NoChange();
2801cb0ef41Sopenharmony_ci  base::Optional<ObjectRef> import_meta =
2811cb0ef41Sopenharmony_ci      module->AsSourceTextModule().import_meta();
2821cb0ef41Sopenharmony_ci  if (!import_meta.has_value()) return NoChange();
2831cb0ef41Sopenharmony_ci  if (!import_meta->IsJSObject()) {
2841cb0ef41Sopenharmony_ci    DCHECK(import_meta->IsTheHole());
2851cb0ef41Sopenharmony_ci    // The import.meta object has not yet been created. Let JSGenericLowering
2861cb0ef41Sopenharmony_ci    // replace the operator with a runtime call.
2871cb0ef41Sopenharmony_ci    return NoChange();
2881cb0ef41Sopenharmony_ci  }
2891cb0ef41Sopenharmony_ci
2901cb0ef41Sopenharmony_ci  Node* import_meta_const = jsgraph()->Constant(*import_meta);
2911cb0ef41Sopenharmony_ci  ReplaceWithValue(node, import_meta_const);
2921cb0ef41Sopenharmony_ci  return Changed(import_meta_const);
2931cb0ef41Sopenharmony_ci}
2941cb0ef41Sopenharmony_ci
2951cb0ef41Sopenharmony_ciIsolate* JSContextSpecialization::isolate() const {
2961cb0ef41Sopenharmony_ci  return jsgraph()->isolate();
2971cb0ef41Sopenharmony_ci}
2981cb0ef41Sopenharmony_ci
2991cb0ef41Sopenharmony_ci}  // namespace compiler
3001cb0ef41Sopenharmony_ci}  // namespace internal
3011cb0ef41Sopenharmony_ci}  // namespace v8
302