Home | History | Annotate | Download | only in compiler
      1 // Copyright 2015 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/compiler/js-global-object-specialization.h"
      6 
      7 #include "src/compilation-dependencies.h"
      8 #include "src/compiler/access-builder.h"
      9 #include "src/compiler/common-operator.h"
     10 #include "src/compiler/js-graph.h"
     11 #include "src/compiler/js-operator.h"
     12 #include "src/compiler/node-properties.h"
     13 #include "src/compiler/simplified-operator.h"
     14 #include "src/lookup.h"
     15 #include "src/objects-inl.h"  // TODO(mstarzinger): Temporary cycle breaker!
     16 #include "src/type-cache.h"
     17 
     18 namespace v8 {
     19 namespace internal {
     20 namespace compiler {
     21 
     22 struct JSGlobalObjectSpecialization::ScriptContextTableLookupResult {
     23   Handle<Context> context;
     24   bool immutable;
     25   int index;
     26 };
     27 
     28 
     29 JSGlobalObjectSpecialization::JSGlobalObjectSpecialization(
     30     Editor* editor, JSGraph* jsgraph, Flags flags,
     31     MaybeHandle<Context> native_context, CompilationDependencies* dependencies)
     32     : AdvancedReducer(editor),
     33       jsgraph_(jsgraph),
     34       flags_(flags),
     35       native_context_(native_context),
     36       dependencies_(dependencies),
     37       type_cache_(TypeCache::Get()) {}
     38 
     39 
     40 Reduction JSGlobalObjectSpecialization::Reduce(Node* node) {
     41   switch (node->opcode()) {
     42     case IrOpcode::kJSLoadGlobal:
     43       return ReduceJSLoadGlobal(node);
     44     case IrOpcode::kJSStoreGlobal:
     45       return ReduceJSStoreGlobal(node);
     46     default:
     47       break;
     48   }
     49   return NoChange();
     50 }
     51 
     52 
     53 Reduction JSGlobalObjectSpecialization::ReduceJSLoadGlobal(Node* node) {
     54   DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode());
     55   Handle<Name> name = LoadGlobalParametersOf(node->op()).name();
     56   Node* effect = NodeProperties::GetEffectInput(node);
     57   Node* control = NodeProperties::GetControlInput(node);
     58 
     59   // Retrieve the global object from the given {node}.
     60   Handle<JSGlobalObject> global_object;
     61   if (!GetGlobalObject(node).ToHandle(&global_object)) return NoChange();
     62 
     63   // Try to lookup the name on the script context table first (lexical scoping).
     64   ScriptContextTableLookupResult result;
     65   if (LookupInScriptContextTable(global_object, name, &result)) {
     66     if (result.context->is_the_hole(result.index)) return NoChange();
     67     Node* context = jsgraph()->HeapConstant(result.context);
     68     Node* value = effect = graph()->NewNode(
     69         javascript()->LoadContext(0, result.index, result.immutable), context,
     70         context, effect);
     71     ReplaceWithValue(node, value, effect);
     72     return Replace(value);
     73   }
     74 
     75   // Lookup on the global object instead.  We only deal with own data
     76   // properties of the global object here (represented as PropertyCell).
     77   LookupIterator it(global_object, name, LookupIterator::OWN);
     78   if (it.state() != LookupIterator::DATA) return NoChange();
     79   Handle<PropertyCell> property_cell = it.GetPropertyCell();
     80   PropertyDetails property_details = property_cell->property_details();
     81   Handle<Object> property_cell_value(property_cell->value(), isolate());
     82 
     83   // Load from non-configurable, read-only data property on the global
     84   // object can be constant-folded, even without deoptimization support.
     85   if (!property_details.IsConfigurable() && property_details.IsReadOnly()) {
     86     Node* value = jsgraph()->Constant(property_cell_value);
     87     ReplaceWithValue(node, value);
     88     return Replace(value);
     89   }
     90 
     91   // Load from non-configurable, data property on the global can be lowered to
     92   // a field load, even without deoptimization, because the property cannot be
     93   // deleted or reconfigured to an accessor/interceptor property.  Yet, if
     94   // deoptimization support is available, we can constant-fold certain global
     95   // properties or at least lower them to field loads annotated with more
     96   // precise type feedback.
     97   Type* property_cell_value_type = Type::Tagged();
     98   if (flags() & kDeoptimizationEnabled) {
     99     // Record a code dependency on the cell if we can benefit from the
    100     // additional feedback, or the global property is configurable (i.e.
    101     // can be deleted or reconfigured to an accessor property).
    102     if (property_details.cell_type() != PropertyCellType::kMutable ||
    103         property_details.IsConfigurable()) {
    104       dependencies()->AssumePropertyCell(property_cell);
    105     }
    106 
    107     // Load from constant/undefined global property can be constant-folded.
    108     if ((property_details.cell_type() == PropertyCellType::kConstant ||
    109          property_details.cell_type() == PropertyCellType::kUndefined)) {
    110       Node* value = jsgraph()->Constant(property_cell_value);
    111       ReplaceWithValue(node, value);
    112       return Replace(value);
    113     }
    114 
    115     // Load from constant type cell can benefit from type feedback.
    116     if (property_details.cell_type() == PropertyCellType::kConstantType) {
    117       // Compute proper type based on the current value in the cell.
    118       if (property_cell_value->IsSmi()) {
    119         property_cell_value_type = type_cache_.kSmi;
    120       } else if (property_cell_value->IsNumber()) {
    121         property_cell_value_type = type_cache_.kHeapNumber;
    122       } else {
    123         Handle<Map> property_cell_value_map(
    124             Handle<HeapObject>::cast(property_cell_value)->map(), isolate());
    125         property_cell_value_type =
    126             Type::Class(property_cell_value_map, graph()->zone());
    127       }
    128     }
    129   } else if (property_details.IsConfigurable()) {
    130     // Access to configurable global properties requires deoptimization support.
    131     return NoChange();
    132   }
    133   Node* value = effect = graph()->NewNode(
    134       simplified()->LoadField(
    135           AccessBuilder::ForPropertyCellValue(property_cell_value_type)),
    136       jsgraph()->HeapConstant(property_cell), effect, control);
    137   ReplaceWithValue(node, value, effect, control);
    138   return Replace(value);
    139 }
    140 
    141 
    142 Reduction JSGlobalObjectSpecialization::ReduceJSStoreGlobal(Node* node) {
    143   DCHECK_EQ(IrOpcode::kJSStoreGlobal, node->opcode());
    144   Handle<Name> name = StoreGlobalParametersOf(node->op()).name();
    145   Node* value = NodeProperties::GetValueInput(node, 0);
    146   Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
    147   Node* effect = NodeProperties::GetEffectInput(node);
    148   Node* control = NodeProperties::GetControlInput(node);
    149 
    150   // Retrieve the global object from the given {node}.
    151   Handle<JSGlobalObject> global_object;
    152   if (!GetGlobalObject(node).ToHandle(&global_object)) return NoChange();
    153 
    154   // Try to lookup the name on the script context table first (lexical scoping).
    155   ScriptContextTableLookupResult result;
    156   if (LookupInScriptContextTable(global_object, name, &result)) {
    157     if (result.context->is_the_hole(result.index)) return NoChange();
    158     if (result.immutable) return NoChange();
    159     Node* context = jsgraph()->HeapConstant(result.context);
    160     effect = graph()->NewNode(javascript()->StoreContext(0, result.index),
    161                               context, value, context, effect, control);
    162     ReplaceWithValue(node, value, effect, control);
    163     return Replace(value);
    164   }
    165 
    166   // Lookup on the global object instead.  We only deal with own data
    167   // properties of the global object here (represented as PropertyCell).
    168   LookupIterator it(global_object, name, LookupIterator::OWN);
    169   if (it.state() != LookupIterator::DATA) return NoChange();
    170   Handle<PropertyCell> property_cell = it.GetPropertyCell();
    171   PropertyDetails property_details = property_cell->property_details();
    172   Handle<Object> property_cell_value(property_cell->value(), isolate());
    173 
    174   // Don't even bother trying to lower stores to read-only data properties.
    175   if (property_details.IsReadOnly()) return NoChange();
    176   switch (property_details.cell_type()) {
    177     case PropertyCellType::kUndefined: {
    178       return NoChange();
    179     }
    180     case PropertyCellType::kConstant: {
    181       // Store to constant property cell requires deoptimization support,
    182       // because we might even need to eager deoptimize for mismatch.
    183       if (!(flags() & kDeoptimizationEnabled)) return NoChange();
    184       dependencies()->AssumePropertyCell(property_cell);
    185       Node* check =
    186           graph()->NewNode(simplified()->ReferenceEqual(Type::Tagged()), value,
    187                            jsgraph()->Constant(property_cell_value));
    188       Node* branch =
    189           graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
    190       Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
    191       Node* deoptimize =
    192           graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kEager),
    193                            frame_state, effect, if_false);
    194       // TODO(bmeurer): This should be on the AdvancedReducer somehow.
    195       NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
    196       control = graph()->NewNode(common()->IfTrue(), branch);
    197       break;
    198     }
    199     case PropertyCellType::kConstantType: {
    200       // Store to constant-type property cell requires deoptimization support,
    201       // because we might even need to eager deoptimize for mismatch.
    202       if (!(flags() & kDeoptimizationEnabled)) return NoChange();
    203       dependencies()->AssumePropertyCell(property_cell);
    204       Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value);
    205       Type* property_cell_value_type = Type::TaggedSigned();
    206       if (property_cell_value->IsHeapObject()) {
    207         // Deoptimize if the {value} is a Smi.
    208         Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse),
    209                                         check, control);
    210         Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
    211         Node* deoptimize =
    212             graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kEager),
    213                              frame_state, effect, if_true);
    214         // TODO(bmeurer): This should be on the AdvancedReducer somehow.
    215         NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
    216         control = graph()->NewNode(common()->IfFalse(), branch);
    217 
    218         // Load the {value} map check against the {property_cell} map.
    219         Node* value_map = effect =
    220             graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
    221                              value, effect, control);
    222         Handle<Map> property_cell_value_map(
    223             Handle<HeapObject>::cast(property_cell_value)->map(), isolate());
    224         check = graph()->NewNode(
    225             simplified()->ReferenceEqual(Type::Any()), value_map,
    226             jsgraph()->HeapConstant(property_cell_value_map));
    227         property_cell_value_type = Type::TaggedPointer();
    228       }
    229       Node* branch =
    230           graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
    231       Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
    232       Node* deoptimize =
    233           graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kEager),
    234                            frame_state, effect, if_false);
    235       // TODO(bmeurer): This should be on the AdvancedReducer somehow.
    236       NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
    237       control = graph()->NewNode(common()->IfTrue(), branch);
    238       effect = graph()->NewNode(
    239           simplified()->StoreField(
    240               AccessBuilder::ForPropertyCellValue(property_cell_value_type)),
    241           jsgraph()->HeapConstant(property_cell), value, effect, control);
    242       break;
    243     }
    244     case PropertyCellType::kMutable: {
    245       // Store to non-configurable, data property on the global can be lowered
    246       // to a field store, even without deoptimization, because the property
    247       // cannot be deleted or reconfigured to an accessor/interceptor property.
    248       if (property_details.IsConfigurable()) {
    249         // With deoptimization support, we can lower stores even to configurable
    250         // data properties on the global object, by adding a code dependency on
    251         // the cell.
    252         if (!(flags() & kDeoptimizationEnabled)) return NoChange();
    253         dependencies()->AssumePropertyCell(property_cell);
    254       }
    255       effect = graph()->NewNode(
    256           simplified()->StoreField(AccessBuilder::ForPropertyCellValue()),
    257           jsgraph()->HeapConstant(property_cell), value, effect, control);
    258       break;
    259     }
    260   }
    261   ReplaceWithValue(node, value, effect, control);
    262   return Replace(value);
    263 }
    264 
    265 
    266 MaybeHandle<JSGlobalObject> JSGlobalObjectSpecialization::GetGlobalObject(
    267     Node* node) {
    268   Node* const context = NodeProperties::GetContextInput(node);
    269   return NodeProperties::GetSpecializationGlobalObject(context,
    270                                                        native_context());
    271 }
    272 
    273 
    274 bool JSGlobalObjectSpecialization::LookupInScriptContextTable(
    275     Handle<JSGlobalObject> global_object, Handle<Name> name,
    276     ScriptContextTableLookupResult* result) {
    277   if (!name->IsString()) return false;
    278   Handle<ScriptContextTable> script_context_table(
    279       global_object->native_context()->script_context_table(), isolate());
    280   ScriptContextTable::LookupResult lookup_result;
    281   if (!ScriptContextTable::Lookup(script_context_table,
    282                                   Handle<String>::cast(name), &lookup_result)) {
    283     return false;
    284   }
    285   Handle<Context> script_context = ScriptContextTable::GetContext(
    286       script_context_table, lookup_result.context_index);
    287   result->context = script_context;
    288   result->immutable = IsImmutableVariableMode(lookup_result.mode);
    289   result->index = lookup_result.slot_index;
    290   return true;
    291 }
    292 
    293 
    294 Graph* JSGlobalObjectSpecialization::graph() const {
    295   return jsgraph()->graph();
    296 }
    297 
    298 
    299 Isolate* JSGlobalObjectSpecialization::isolate() const {
    300   return jsgraph()->isolate();
    301 }
    302 
    303 
    304 CommonOperatorBuilder* JSGlobalObjectSpecialization::common() const {
    305   return jsgraph()->common();
    306 }
    307 
    308 
    309 JSOperatorBuilder* JSGlobalObjectSpecialization::javascript() const {
    310   return jsgraph()->javascript();
    311 }
    312 
    313 
    314 SimplifiedOperatorBuilder* JSGlobalObjectSpecialization::simplified() const {
    315   return jsgraph()->simplified();
    316 }
    317 
    318 }  // namespace compiler
    319 }  // namespace internal
    320 }  // namespace v8
    321