| Index: src/compiler/js-native-context-specialization.cc
|
| diff --git a/src/compiler/js-native-context-specialization.cc b/src/compiler/js-native-context-specialization.cc
|
| index b1b27def6a9278f53a31811ed8ca6e5b2ce672f3..9936252f45459318690e21112149d4112360c8cc 100644
|
| --- a/src/compiler/js-native-context-specialization.cc
|
| +++ b/src/compiler/js-native-context-specialization.cc
|
| @@ -55,6 +55,12 @@ bool HasOnlyStringMaps(T const& maps) {
|
|
|
| } // namespace
|
|
|
| +struct JSNativeContextSpecialization::ScriptContextTableLookupResult {
|
| + Handle<Context> context;
|
| + bool immutable;
|
| + int index;
|
| +};
|
| +
|
| JSNativeContextSpecialization::JSNativeContextSpecialization(
|
| Editor* editor, JSGraph* jsgraph, Flags flags,
|
| Handle<Context> native_context, CompilationDependencies* dependencies,
|
| @@ -62,6 +68,8 @@ JSNativeContextSpecialization::JSNativeContextSpecialization(
|
| : AdvancedReducer(editor),
|
| jsgraph_(jsgraph),
|
| flags_(flags),
|
| + global_object_(native_context->global_object()),
|
| + global_proxy_(JSGlobalProxy::cast(native_context->global_proxy())),
|
| native_context_(native_context),
|
| dependencies_(dependencies),
|
| zone_(zone),
|
| @@ -77,6 +85,10 @@ Reduction JSNativeContextSpecialization::Reduce(Node* node) {
|
| return ReduceJSOrdinaryHasInstance(node);
|
| case IrOpcode::kJSLoadContext:
|
| return ReduceJSLoadContext(node);
|
| + case IrOpcode::kJSLoadGlobal:
|
| + return ReduceJSLoadGlobal(node);
|
| + case IrOpcode::kJSStoreGlobal:
|
| + return ReduceJSStoreGlobal(node);
|
| case IrOpcode::kJSLoadNamed:
|
| return ReduceJSLoadNamed(node);
|
| case IrOpcode::kJSStoreNamed:
|
| @@ -255,6 +267,250 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadContext(Node* node) {
|
| return NoChange();
|
| }
|
|
|
| +namespace {
|
| +
|
| +FieldAccess ForPropertyCellValue(MachineRepresentation representation,
|
| + Type* type, MaybeHandle<Map> map,
|
| + Handle<Name> name) {
|
| + WriteBarrierKind kind = kFullWriteBarrier;
|
| + if (representation == MachineRepresentation::kTaggedSigned) {
|
| + kind = kNoWriteBarrier;
|
| + } else if (representation == MachineRepresentation::kTaggedPointer) {
|
| + kind = kPointerWriteBarrier;
|
| + }
|
| + MachineType r = MachineType::TypeForRepresentation(representation);
|
| + FieldAccess access = {
|
| + kTaggedBase, PropertyCell::kValueOffset, name, map, type, r, kind};
|
| + return access;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
|
| + Node* node, Node* receiver, Node* value, Handle<Name> name,
|
| + AccessMode access_mode) {
|
| + Node* effect = NodeProperties::GetEffectInput(node);
|
| + Node* control = NodeProperties::GetControlInput(node);
|
| +
|
| + // Lookup on the global object. We only deal with own data properties
|
| + // of the global object here (represented as PropertyCell).
|
| + LookupIterator it(global_object(), name, LookupIterator::OWN);
|
| + it.TryLookupCachedProperty();
|
| + if (it.state() != LookupIterator::DATA) return NoChange();
|
| + if (!it.GetHolder<JSObject>()->IsJSGlobalObject()) return NoChange();
|
| + Handle<PropertyCell> property_cell = it.GetPropertyCell();
|
| + PropertyDetails property_details = property_cell->property_details();
|
| + Handle<Object> property_cell_value(property_cell->value(), isolate());
|
| + PropertyCellType property_cell_type = property_details.cell_type();
|
| +
|
| + // We have additional constraints for stores.
|
| + if (access_mode == AccessMode::kStore) {
|
| + if (property_details.IsReadOnly()) {
|
| + // Don't even bother trying to lower stores to read-only data properties.
|
| + return NoChange();
|
| + } else if (property_cell_type == PropertyCellType::kUndefined) {
|
| + // There's no fast-path for dealing with undefined property cells.
|
| + return NoChange();
|
| + } else if (property_cell_type == PropertyCellType::kConstantType) {
|
| + // There's also no fast-path to store to a global cell which pretended
|
| + // to be stable, but is no longer stable now.
|
| + if (property_cell_value->IsHeapObject() &&
|
| + !Handle<HeapObject>::cast(property_cell_value)->map()->is_stable()) {
|
| + return NoChange();
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Check if we have a {receiver} to validate. If so, we need to check that
|
| + // the {receiver} is actually the JSGlobalProxy for the native context that
|
| + // we are specializing to.
|
| + if (receiver != nullptr) {
|
| + Node* check = graph()->NewNode(simplified()->ReferenceEqual(), receiver,
|
| + jsgraph()->HeapConstant(global_proxy()));
|
| + effect = graph()->NewNode(simplified()->CheckIf(), check, effect, control);
|
| + }
|
| +
|
| + if (access_mode == AccessMode::kLoad) {
|
| + // Load from non-configurable, read-only data property on the global
|
| + // object can be constant-folded, even without deoptimization support.
|
| + if (!property_details.IsConfigurable() && property_details.IsReadOnly()) {
|
| + value = jsgraph()->Constant(property_cell_value);
|
| + } else {
|
| + // Record a code dependency on the cell if we can benefit from the
|
| + // additional feedback, or the global property is configurable (i.e.
|
| + // can be deleted or reconfigured to an accessor property).
|
| + if (property_details.cell_type() != PropertyCellType::kMutable ||
|
| + property_details.IsConfigurable()) {
|
| + dependencies()->AssumePropertyCell(property_cell);
|
| + }
|
| +
|
| + // Load from constant/undefined global property can be constant-folded.
|
| + if (property_details.cell_type() == PropertyCellType::kConstant ||
|
| + property_details.cell_type() == PropertyCellType::kUndefined) {
|
| + value = jsgraph()->Constant(property_cell_value);
|
| + } else {
|
| + // Load from constant type cell can benefit from type feedback.
|
| + MaybeHandle<Map> map;
|
| + Type* property_cell_value_type = Type::NonInternal();
|
| + MachineRepresentation representation = MachineRepresentation::kTagged;
|
| + if (property_details.cell_type() == PropertyCellType::kConstantType) {
|
| + // Compute proper type based on the current value in the cell.
|
| + if (property_cell_value->IsSmi()) {
|
| + property_cell_value_type = Type::SignedSmall();
|
| + representation = MachineRepresentation::kTaggedSigned;
|
| + } else if (property_cell_value->IsNumber()) {
|
| + property_cell_value_type = Type::Number();
|
| + representation = MachineRepresentation::kTaggedPointer;
|
| + } else {
|
| + Handle<Map> property_cell_value_map(
|
| + Handle<HeapObject>::cast(property_cell_value)->map(),
|
| + isolate());
|
| + property_cell_value_type = Type::For(property_cell_value_map);
|
| + representation = MachineRepresentation::kTaggedPointer;
|
| +
|
| + // We can only use the property cell value map for map check
|
| + // elimination if it's stable, i.e. the HeapObject wasn't
|
| + // mutated without the cell state being updated.
|
| + if (property_cell_value_map->is_stable()) {
|
| + dependencies()->AssumeMapStable(property_cell_value_map);
|
| + map = property_cell_value_map;
|
| + }
|
| + }
|
| + }
|
| + value = effect = graph()->NewNode(
|
| + simplified()->LoadField(ForPropertyCellValue(
|
| + representation, property_cell_value_type, map, name)),
|
| + jsgraph()->HeapConstant(property_cell), effect, control);
|
| + }
|
| + }
|
| + } else {
|
| + DCHECK_EQ(AccessMode::kStore, access_mode);
|
| + DCHECK(!property_details.IsReadOnly());
|
| + switch (property_details.cell_type()) {
|
| + case PropertyCellType::kUndefined: {
|
| + UNREACHABLE();
|
| + break;
|
| + }
|
| + case PropertyCellType::kConstant: {
|
| + // Record a code dependency on the cell, and just deoptimize if the new
|
| + // value doesn't match the previous value stored inside the cell.
|
| + dependencies()->AssumePropertyCell(property_cell);
|
| + Node* check =
|
| + graph()->NewNode(simplified()->ReferenceEqual(), value,
|
| + jsgraph()->Constant(property_cell_value));
|
| + effect =
|
| + graph()->NewNode(simplified()->CheckIf(), check, effect, control);
|
| + break;
|
| + }
|
| + case PropertyCellType::kConstantType: {
|
| + // Record a code dependency on the cell, and just deoptimize if the new
|
| + // values' type doesn't match the type of the previous value in the
|
| + // cell.
|
| + dependencies()->AssumePropertyCell(property_cell);
|
| + Type* property_cell_value_type;
|
| + MachineRepresentation representation = MachineRepresentation::kTagged;
|
| + if (property_cell_value->IsHeapObject()) {
|
| + // We cannot do anything if the {property_cell_value}s map is no
|
| + // longer stable.
|
| + Handle<Map> property_cell_value_map(
|
| + Handle<HeapObject>::cast(property_cell_value)->map(), isolate());
|
| + DCHECK(property_cell_value_map->is_stable());
|
| + dependencies()->AssumeMapStable(property_cell_value_map);
|
| +
|
| + // Check that the {value} is a HeapObject.
|
| + value = effect = graph()->NewNode(simplified()->CheckHeapObject(),
|
| + value, effect, control);
|
| +
|
| + // Check {value} map agains the {property_cell} map.
|
| + effect =
|
| + graph()->NewNode(simplified()->CheckMaps(
|
| + CheckMapsFlag::kNone,
|
| + ZoneHandleSet<Map>(property_cell_value_map)),
|
| + value, effect, control);
|
| + property_cell_value_type = Type::OtherInternal();
|
| + representation = MachineRepresentation::kTaggedPointer;
|
| + } else {
|
| + // Check that the {value} is a Smi.
|
| + value = effect = graph()->NewNode(simplified()->CheckSmi(), value,
|
| + effect, control);
|
| + property_cell_value_type = Type::SignedSmall();
|
| + representation = MachineRepresentation::kTaggedSigned;
|
| + }
|
| + effect = graph()->NewNode(simplified()->StoreField(ForPropertyCellValue(
|
| + representation, property_cell_value_type,
|
| + MaybeHandle<Map>(), name)),
|
| + jsgraph()->HeapConstant(property_cell), value,
|
| + effect, control);
|
| + break;
|
| + }
|
| + case PropertyCellType::kMutable: {
|
| + // Record a code dependency on the cell, and just deoptimize if the
|
| + // property ever becomes read-only.
|
| + dependencies()->AssumePropertyCell(property_cell);
|
| + effect = graph()->NewNode(
|
| + simplified()->StoreField(ForPropertyCellValue(
|
| + MachineRepresentation::kTagged, Type::NonInternal(),
|
| + MaybeHandle<Map>(), name)),
|
| + jsgraph()->HeapConstant(property_cell), value, effect, control);
|
| + break;
|
| + }
|
| + }
|
| + }
|
| +
|
| + ReplaceWithValue(node, value, effect, control);
|
| + return Replace(value);
|
| +}
|
| +
|
| +Reduction JSNativeContextSpecialization::ReduceJSLoadGlobal(Node* node) {
|
| + DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode());
|
| + Handle<Name> name = LoadGlobalParametersOf(node->op()).name();
|
| + Node* effect = NodeProperties::GetEffectInput(node);
|
| +
|
| + // Try to lookup the name on the script context table first (lexical scoping).
|
| + ScriptContextTableLookupResult result;
|
| + if (LookupInScriptContextTable(name, &result)) {
|
| + if (result.context->is_the_hole(isolate(), result.index)) return NoChange();
|
| + Node* context = jsgraph()->HeapConstant(result.context);
|
| + Node* value = effect = graph()->NewNode(
|
| + javascript()->LoadContext(0, result.index, result.immutable), context,
|
| + effect);
|
| + ReplaceWithValue(node, value, effect);
|
| + return Replace(value);
|
| + }
|
| +
|
| + // Not much we can do if deoptimization support is disabled.
|
| + if (!(flags() & kDeoptimizationEnabled)) return NoChange();
|
| +
|
| + // Lookup the {name} on the global object instead.
|
| + return ReduceGlobalAccess(node, nullptr, nullptr, name, AccessMode::kLoad);
|
| +}
|
| +
|
| +Reduction JSNativeContextSpecialization::ReduceJSStoreGlobal(Node* node) {
|
| + DCHECK_EQ(IrOpcode::kJSStoreGlobal, node->opcode());
|
| + Handle<Name> name = StoreGlobalParametersOf(node->op()).name();
|
| + Node* value = NodeProperties::GetValueInput(node, 0);
|
| + Node* effect = NodeProperties::GetEffectInput(node);
|
| + Node* control = NodeProperties::GetControlInput(node);
|
| +
|
| + // Try to lookup the name on the script context table first (lexical scoping).
|
| + ScriptContextTableLookupResult result;
|
| + if (LookupInScriptContextTable(name, &result)) {
|
| + if (result.context->is_the_hole(isolate(), result.index)) return NoChange();
|
| + if (result.immutable) return NoChange();
|
| + Node* context = jsgraph()->HeapConstant(result.context);
|
| + effect = graph()->NewNode(javascript()->StoreContext(0, result.index),
|
| + value, context, effect, control);
|
| + ReplaceWithValue(node, value, effect, control);
|
| + return Replace(value);
|
| + }
|
| +
|
| + // Not much we can do if deoptimization support is disabled.
|
| + if (!(flags() & kDeoptimizationEnabled)) return NoChange();
|
| +
|
| + // Lookup the {name} on the global object instead.
|
| + return ReduceGlobalAccess(node, nullptr, value, name, AccessMode::kStore);
|
| +}
|
| +
|
| Reduction JSNativeContextSpecialization::ReduceNamedAccess(
|
| Node* node, Node* value, MapHandleList const& receiver_maps,
|
| Handle<Name> name, AccessMode access_mode, LanguageMode language_mode,
|
| @@ -273,6 +529,20 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
|
| // Not much we can do if deoptimization support is disabled.
|
| if (!(flags() & kDeoptimizationEnabled)) return NoChange();
|
|
|
| + // Check if we have an access o.x or o.x=v where o is the current
|
| + // native contexts' global proxy, and turn that into a direct access
|
| + // to the current native contexts' global object instead.
|
| + if (receiver_maps.length() == 1) {
|
| + Handle<Map> receiver_map = receiver_maps.first();
|
| + if (receiver_map->IsJSGlobalProxyMap()) {
|
| + Context* receiver_context =
|
| + JSFunction::cast(receiver_map->GetConstructor())->native_context();
|
| + if (receiver_context == *native_context()) {
|
| + return ReduceGlobalAccess(node, receiver, value, name, access_mode);
|
| + }
|
| + }
|
| + }
|
| +
|
| // Compute property access infos for the receiver maps.
|
| AccessInfoFactory access_info_factory(dependencies(), native_context(),
|
| graph()->zone());
|
| @@ -487,6 +757,13 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccessFromNexus(
|
| Node* const receiver = NodeProperties::GetValueInput(node, 0);
|
| Node* const effect = NodeProperties::GetEffectInput(node);
|
|
|
| + // Check if we are accessing the current native contexts' global proxy.
|
| + HeapObjectMatcher m(receiver);
|
| + if (m.HasValue() && m.Value().is_identical_to(global_proxy())) {
|
| + // Optimize accesses to the current native contexts' global proxy.
|
| + return ReduceGlobalAccess(node, nullptr, value, name, access_mode);
|
| + }
|
| +
|
| // Check if the {nexus} reports type feedback for the IC.
|
| if (nexus.IsUninitialized()) {
|
| if ((flags() & kDeoptimizationEnabled) &&
|
| @@ -517,7 +794,6 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccessFromNexus(
|
| language_mode, nexus.vector_handle(), nexus.slot());
|
| }
|
|
|
| -
|
| Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) {
|
| DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode());
|
| NamedAccess const& p = NamedAccessOf(node->op());
|
| @@ -1942,6 +2218,24 @@ MaybeHandle<Map> JSNativeContextSpecialization::InferReceiverRootMap(
|
| return MaybeHandle<Map>();
|
| }
|
|
|
| +bool JSNativeContextSpecialization::LookupInScriptContextTable(
|
| + Handle<Name> name, ScriptContextTableLookupResult* result) {
|
| + if (!name->IsString()) return false;
|
| + Handle<ScriptContextTable> script_context_table(
|
| + global_object()->native_context()->script_context_table(), isolate());
|
| + ScriptContextTable::LookupResult lookup_result;
|
| + if (!ScriptContextTable::Lookup(script_context_table,
|
| + Handle<String>::cast(name), &lookup_result)) {
|
| + return false;
|
| + }
|
| + Handle<Context> script_context = ScriptContextTable::GetContext(
|
| + script_context_table, lookup_result.context_index);
|
| + result->context = script_context;
|
| + result->immutable = lookup_result.mode == CONST;
|
| + result->index = lookup_result.slot_index;
|
| + return true;
|
| +}
|
| +
|
| Graph* JSNativeContextSpecialization::graph() const {
|
| return jsgraph()->graph();
|
| }
|
|
|