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/compiler/type-cache.h" 15 #include "src/lookup.h" 16 #include "src/objects-inl.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 JSGlobalObjectSpecialization::JSGlobalObjectSpecialization( 29 Editor* editor, JSGraph* jsgraph, Handle<JSGlobalObject> global_object, 30 CompilationDependencies* dependencies) 31 : AdvancedReducer(editor), 32 jsgraph_(jsgraph), 33 global_object_(global_object), 34 dependencies_(dependencies), 35 type_cache_(TypeCache::Get()) {} 36 37 Reduction JSGlobalObjectSpecialization::Reduce(Node* node) { 38 switch (node->opcode()) { 39 case IrOpcode::kJSLoadGlobal: 40 return ReduceJSLoadGlobal(node); 41 case IrOpcode::kJSStoreGlobal: 42 return ReduceJSStoreGlobal(node); 43 default: 44 break; 45 } 46 return NoChange(); 47 } 48 49 namespace { 50 51 FieldAccess ForPropertyCellValue(MachineRepresentation representation, 52 Type* type, Handle<Name> name) { 53 WriteBarrierKind kind = kFullWriteBarrier; 54 if (representation == MachineRepresentation::kTaggedSigned) { 55 kind = kNoWriteBarrier; 56 } else if (representation == MachineRepresentation::kTaggedPointer) { 57 kind = kPointerWriteBarrier; 58 } 59 MachineType r = MachineType::TypeForRepresentation(representation); 60 FieldAccess access = {kTaggedBase, PropertyCell::kValueOffset, name, type, r, 61 kind}; 62 return access; 63 } 64 } // namespace 65 66 Reduction JSGlobalObjectSpecialization::ReduceJSLoadGlobal(Node* node) { 67 DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode()); 68 Handle<Name> name = LoadGlobalParametersOf(node->op()).name(); 69 Node* effect = NodeProperties::GetEffectInput(node); 70 Node* control = NodeProperties::GetControlInput(node); 71 72 // Try to lookup the name on the script context table first (lexical scoping). 73 ScriptContextTableLookupResult result; 74 if (LookupInScriptContextTable(name, &result)) { 75 if (result.context->is_the_hole(isolate(), result.index)) return NoChange(); 76 Node* context = jsgraph()->HeapConstant(result.context); 77 Node* value = effect = graph()->NewNode( 78 javascript()->LoadContext(0, result.index, result.immutable), context, 79 context, effect); 80 ReplaceWithValue(node, value, effect); 81 return Replace(value); 82 } 83 84 // Lookup on the global object instead. We only deal with own data 85 // properties of the global object here (represented as PropertyCell). 86 LookupIterator it(global_object(), name, LookupIterator::OWN); 87 if (it.state() != LookupIterator::DATA) return NoChange(); 88 if (!it.GetHolder<JSObject>()->IsJSGlobalObject()) return NoChange(); 89 Handle<PropertyCell> property_cell = it.GetPropertyCell(); 90 PropertyDetails property_details = property_cell->property_details(); 91 Handle<Object> property_cell_value(property_cell->value(), isolate()); 92 93 // Load from non-configurable, read-only data property on the global 94 // object can be constant-folded, even without deoptimization support. 95 if (!property_details.IsConfigurable() && property_details.IsReadOnly()) { 96 Node* value = jsgraph()->Constant(property_cell_value); 97 ReplaceWithValue(node, value); 98 return Replace(value); 99 } 100 101 // Record a code dependency on the cell if we can benefit from the 102 // additional feedback, or the global property is configurable (i.e. 103 // can be deleted or reconfigured to an accessor property). 104 if (property_details.cell_type() != PropertyCellType::kMutable || 105 property_details.IsConfigurable()) { 106 dependencies()->AssumePropertyCell(property_cell); 107 } 108 109 // Load from constant/undefined global property can be constant-folded. 110 if (property_details.cell_type() == PropertyCellType::kConstant || 111 property_details.cell_type() == PropertyCellType::kUndefined) { 112 Node* value = jsgraph()->Constant(property_cell_value); 113 ReplaceWithValue(node, value); 114 return Replace(value); 115 } 116 117 // Load from constant type cell can benefit from type feedback. 118 Type* property_cell_value_type = Type::NonInternal(); 119 MachineRepresentation representation = MachineRepresentation::kTagged; 120 if (property_details.cell_type() == PropertyCellType::kConstantType) { 121 // Compute proper type based on the current value in the cell. 122 if (property_cell_value->IsSmi()) { 123 property_cell_value_type = Type::SignedSmall(); 124 representation = MachineRepresentation::kTaggedSigned; 125 } else if (property_cell_value->IsNumber()) { 126 property_cell_value_type = Type::Number(); 127 representation = MachineRepresentation::kTaggedPointer; 128 } else { 129 // TODO(turbofan): Track the property_cell_value_map on the FieldAccess 130 // below and use it in LoadElimination to eliminate map checks. 131 Handle<Map> property_cell_value_map( 132 Handle<HeapObject>::cast(property_cell_value)->map(), isolate()); 133 property_cell_value_type = Type::For(property_cell_value_map); 134 representation = MachineRepresentation::kTaggedPointer; 135 } 136 } 137 Node* value = effect = 138 graph()->NewNode(simplified()->LoadField(ForPropertyCellValue( 139 representation, property_cell_value_type, name)), 140 jsgraph()->HeapConstant(property_cell), effect, control); 141 ReplaceWithValue(node, value, effect, control); 142 return Replace(value); 143 } 144 145 146 Reduction JSGlobalObjectSpecialization::ReduceJSStoreGlobal(Node* node) { 147 DCHECK_EQ(IrOpcode::kJSStoreGlobal, node->opcode()); 148 Handle<Name> name = StoreGlobalParametersOf(node->op()).name(); 149 Node* value = NodeProperties::GetValueInput(node, 0); 150 Node* effect = NodeProperties::GetEffectInput(node); 151 Node* control = NodeProperties::GetControlInput(node); 152 153 // Try to lookup the name on the script context table first (lexical scoping). 154 ScriptContextTableLookupResult result; 155 if (LookupInScriptContextTable(name, &result)) { 156 if (result.context->is_the_hole(isolate(), result.index)) return NoChange(); 157 if (result.immutable) return NoChange(); 158 Node* context = jsgraph()->HeapConstant(result.context); 159 effect = graph()->NewNode(javascript()->StoreContext(0, result.index), 160 context, value, context, effect, control); 161 ReplaceWithValue(node, value, effect, control); 162 return Replace(value); 163 } 164 165 // Lookup on the global object instead. We only deal with own data 166 // properties of the global object here (represented as PropertyCell). 167 LookupIterator it(global_object(), name, LookupIterator::OWN); 168 if (it.state() != LookupIterator::DATA) return NoChange(); 169 if (!it.GetHolder<JSObject>()->IsJSGlobalObject()) 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 // Record a code dependency on the cell, and just deoptimize if the new 182 // value doesn't match the previous value stored inside the cell. 183 dependencies()->AssumePropertyCell(property_cell); 184 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), value, 185 jsgraph()->Constant(property_cell_value)); 186 effect = 187 graph()->NewNode(simplified()->CheckIf(), check, effect, control); 188 break; 189 } 190 case PropertyCellType::kConstantType: { 191 // Record a code dependency on the cell, and just deoptimize if the new 192 // values' type doesn't match the type of the previous value in the cell. 193 dependencies()->AssumePropertyCell(property_cell); 194 Type* property_cell_value_type; 195 MachineRepresentation representation = MachineRepresentation::kTagged; 196 if (property_cell_value->IsHeapObject()) { 197 // We cannot do anything if the {property_cell_value}s map is no 198 // longer stable. 199 Handle<Map> property_cell_value_map( 200 Handle<HeapObject>::cast(property_cell_value)->map(), isolate()); 201 if (!property_cell_value_map->is_stable()) return NoChange(); 202 dependencies()->AssumeMapStable(property_cell_value_map); 203 204 // Check that the {value} is a HeapObject. 205 value = effect = graph()->NewNode(simplified()->CheckHeapObject(), 206 value, effect, control); 207 208 // Check {value} map agains the {property_cell} map. 209 effect = graph()->NewNode( 210 simplified()->CheckMaps(1), value, 211 jsgraph()->HeapConstant(property_cell_value_map), effect, control); 212 property_cell_value_type = Type::OtherInternal(); 213 representation = MachineRepresentation::kTaggedPointer; 214 } else { 215 // Check that the {value} is a Smi. 216 value = effect = 217 graph()->NewNode(simplified()->CheckSmi(), value, effect, control); 218 property_cell_value_type = Type::SignedSmall(); 219 representation = MachineRepresentation::kTaggedSigned; 220 } 221 effect = graph()->NewNode( 222 simplified()->StoreField(ForPropertyCellValue( 223 representation, property_cell_value_type, name)), 224 jsgraph()->HeapConstant(property_cell), value, effect, control); 225 break; 226 } 227 case PropertyCellType::kMutable: { 228 // Store to non-configurable, data property on the global can be lowered 229 // to a field store, even without recording a code dependency on the cell, 230 // because the property cannot be deleted or reconfigured to an accessor 231 // or interceptor property. 232 if (property_details.IsConfigurable()) { 233 // Protect lowering by recording a code dependency on the cell. 234 dependencies()->AssumePropertyCell(property_cell); 235 } 236 effect = graph()->NewNode( 237 simplified()->StoreField(ForPropertyCellValue( 238 MachineRepresentation::kTagged, Type::NonInternal(), name)), 239 jsgraph()->HeapConstant(property_cell), value, effect, control); 240 break; 241 } 242 } 243 ReplaceWithValue(node, value, effect, control); 244 return Replace(value); 245 } 246 247 bool JSGlobalObjectSpecialization::LookupInScriptContextTable( 248 Handle<Name> name, ScriptContextTableLookupResult* result) { 249 if (!name->IsString()) return false; 250 Handle<ScriptContextTable> script_context_table( 251 global_object()->native_context()->script_context_table(), isolate()); 252 ScriptContextTable::LookupResult lookup_result; 253 if (!ScriptContextTable::Lookup(script_context_table, 254 Handle<String>::cast(name), &lookup_result)) { 255 return false; 256 } 257 Handle<Context> script_context = ScriptContextTable::GetContext( 258 script_context_table, lookup_result.context_index); 259 result->context = script_context; 260 result->immutable = lookup_result.mode == CONST; 261 result->index = lookup_result.slot_index; 262 return true; 263 } 264 265 Graph* JSGlobalObjectSpecialization::graph() const { 266 return jsgraph()->graph(); 267 } 268 269 Isolate* JSGlobalObjectSpecialization::isolate() const { 270 return jsgraph()->isolate(); 271 } 272 273 CommonOperatorBuilder* JSGlobalObjectSpecialization::common() const { 274 return jsgraph()->common(); 275 } 276 277 JSOperatorBuilder* JSGlobalObjectSpecialization::javascript() const { 278 return jsgraph()->javascript(); 279 } 280 281 SimplifiedOperatorBuilder* JSGlobalObjectSpecialization::simplified() const { 282 return jsgraph()->simplified(); 283 } 284 285 } // namespace compiler 286 } // namespace internal 287 } // namespace v8 288