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