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