| OLD | NEW |
| (Empty) |
| 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-type-feedback.h" | |
| 6 | |
| 7 #include "src/property-details.h" | |
| 8 | |
| 9 #include "src/accessors.h" | |
| 10 #include "src/ast.h" | |
| 11 #include "src/compiler.h" | |
| 12 #include "src/type-info.h" | |
| 13 | |
| 14 #include "src/compiler/access-builder.h" | |
| 15 #include "src/compiler/common-operator.h" | |
| 16 #include "src/compiler/frame-states.h" | |
| 17 #include "src/compiler/node-aux-data.h" | |
| 18 #include "src/compiler/node-matchers.h" | |
| 19 #include "src/compiler/operator-properties.h" | |
| 20 #include "src/compiler/simplified-operator.h" | |
| 21 | |
| 22 namespace v8 { | |
| 23 namespace internal { | |
| 24 namespace compiler { | |
| 25 | |
| 26 enum LoadOrStore { LOAD, STORE }; | |
| 27 | |
| 28 // TODO(turbofan): fix deoptimization problems | |
| 29 #define ENABLE_FAST_PROPERTY_LOADS false | |
| 30 #define ENABLE_FAST_PROPERTY_STORES false | |
| 31 | |
| 32 JSTypeFeedbackTable::JSTypeFeedbackTable(Zone* zone) | |
| 33 : type_feedback_id_map_(TypeFeedbackIdMap::key_compare(), | |
| 34 TypeFeedbackIdMap::allocator_type(zone)), | |
| 35 feedback_vector_slot_map_(TypeFeedbackIdMap::key_compare(), | |
| 36 TypeFeedbackIdMap::allocator_type(zone)) {} | |
| 37 | |
| 38 | |
| 39 void JSTypeFeedbackTable::Record(Node* node, TypeFeedbackId id) { | |
| 40 type_feedback_id_map_.insert(std::make_pair(node->id(), id)); | |
| 41 } | |
| 42 | |
| 43 | |
| 44 void JSTypeFeedbackTable::Record(Node* node, FeedbackVectorSlot slot) { | |
| 45 feedback_vector_slot_map_.insert(std::make_pair(node->id(), slot)); | |
| 46 } | |
| 47 | |
| 48 | |
| 49 Reduction JSTypeFeedbackSpecializer::Reduce(Node* node) { | |
| 50 switch (node->opcode()) { | |
| 51 case IrOpcode::kJSLoadProperty: | |
| 52 return ReduceJSLoadProperty(node); | |
| 53 case IrOpcode::kJSLoadNamed: | |
| 54 return ReduceJSLoadNamed(node); | |
| 55 case IrOpcode::kJSLoadGlobal: | |
| 56 return ReduceJSLoadGlobal(node); | |
| 57 case IrOpcode::kJSStoreNamed: | |
| 58 return ReduceJSStoreNamed(node); | |
| 59 case IrOpcode::kJSStoreProperty: | |
| 60 return ReduceJSStoreProperty(node); | |
| 61 default: | |
| 62 break; | |
| 63 } | |
| 64 return NoChange(); | |
| 65 } | |
| 66 | |
| 67 | |
| 68 static void AddFieldAccessTypes(FieldAccess* access, | |
| 69 PropertyDetails property_details) { | |
| 70 if (property_details.representation().IsSmi()) { | |
| 71 access->type = Type::SignedSmall(); | |
| 72 access->machine_type = static_cast<MachineType>(kTypeInt32 | kRepTagged); | |
| 73 } else if (property_details.representation().IsDouble()) { | |
| 74 access->type = Type::Number(); | |
| 75 access->machine_type = kMachFloat64; | |
| 76 } | |
| 77 } | |
| 78 | |
| 79 | |
| 80 static bool GetInObjectFieldAccess(LoadOrStore mode, Handle<Map> map, | |
| 81 Handle<Name> name, FieldAccess* access) { | |
| 82 access->base_is_tagged = kTaggedBase; | |
| 83 access->offset = -1; | |
| 84 access->name = name; | |
| 85 access->type = Type::Any(); | |
| 86 access->machine_type = kMachAnyTagged; | |
| 87 | |
| 88 // Check for properties that have accessors but are JSObject fields. | |
| 89 if (Accessors::IsJSObjectFieldAccessor(map, name, &access->offset)) { | |
| 90 // TODO(turbofan): fill in types for special JSObject field accesses. | |
| 91 return true; | |
| 92 } | |
| 93 | |
| 94 // Check if the map is a dictionary. | |
| 95 if (map->is_dictionary_map()) return false; | |
| 96 | |
| 97 // Search the descriptor array. | |
| 98 DescriptorArray* descriptors = map->instance_descriptors(); | |
| 99 int number = descriptors->SearchWithCache(*name, *map); | |
| 100 if (number == DescriptorArray::kNotFound) return false; | |
| 101 PropertyDetails property_details = descriptors->GetDetails(number); | |
| 102 | |
| 103 bool is_smi = property_details.representation().IsSmi(); | |
| 104 bool is_double = property_details.representation().IsDouble(); | |
| 105 | |
| 106 if (property_details.type() != DATA) { | |
| 107 // TODO(turbofan): constant loads and stores. | |
| 108 return false; | |
| 109 } | |
| 110 | |
| 111 // Transfer known types from property details. | |
| 112 AddFieldAccessTypes(access, property_details); | |
| 113 | |
| 114 if (mode == STORE) { | |
| 115 if (property_details.IsReadOnly()) { | |
| 116 // TODO(turbofan): deopt, ignore or throw on readonly stores. | |
| 117 return false; | |
| 118 } | |
| 119 if (is_smi || is_double) { | |
| 120 // TODO(turbofan): check type and deopt for SMI/double stores. | |
| 121 return false; | |
| 122 } | |
| 123 } | |
| 124 | |
| 125 int index = map->instance_descriptors()->GetFieldIndex(number); | |
| 126 FieldIndex field_index = FieldIndex::ForPropertyIndex(*map, index, is_double); | |
| 127 | |
| 128 if (field_index.is_inobject()) { | |
| 129 if (is_double && !map->IsUnboxedDoubleField(field_index)) { | |
| 130 // TODO(turbofan): support for out-of-line (MutableHeapNumber) loads. | |
| 131 return false; | |
| 132 } | |
| 133 access->offset = field_index.offset(); | |
| 134 return true; | |
| 135 } | |
| 136 | |
| 137 // TODO(turbofan): handle out of object properties. | |
| 138 return false; | |
| 139 } | |
| 140 | |
| 141 | |
| 142 Reduction JSTypeFeedbackSpecializer::ReduceJSLoadNamed(Node* node) { | |
| 143 DCHECK(node->opcode() == IrOpcode::kJSLoadNamed); | |
| 144 if (mode() != kDeoptimizationEnabled) return NoChange(); | |
| 145 Node* frame_state_before = GetFrameStateBefore(node); | |
| 146 if (frame_state_before == nullptr) return NoChange(); | |
| 147 | |
| 148 NamedAccess const& p = NamedAccessOf(node->op()); | |
| 149 SmallMapList maps; | |
| 150 | |
| 151 FeedbackVectorSlot slot = js_type_feedback_->FindFeedbackVectorSlot(node); | |
| 152 if (slot.IsInvalid() || | |
| 153 oracle()->LoadInlineCacheState(slot) == UNINITIALIZED) { | |
| 154 // No type feedback ids or the load is uninitialized. | |
| 155 return NoChange(); | |
| 156 } | |
| 157 oracle()->PropertyReceiverTypes(slot, p.name(), &maps); | |
| 158 | |
| 159 Node* receiver = node->InputAt(0); | |
| 160 Node* effect = NodeProperties::GetEffectInput(node); | |
| 161 | |
| 162 if (maps.length() != 1) return NoChange(); // TODO(turbofan): polymorphism | |
| 163 if (!ENABLE_FAST_PROPERTY_LOADS) return NoChange(); | |
| 164 | |
| 165 Handle<Map> map = maps.first(); | |
| 166 FieldAccess field_access; | |
| 167 if (!GetInObjectFieldAccess(LOAD, map, p.name(), &field_access)) { | |
| 168 return NoChange(); | |
| 169 } | |
| 170 | |
| 171 Node* control = NodeProperties::GetControlInput(node); | |
| 172 Node* check_success; | |
| 173 Node* check_failed; | |
| 174 BuildMapCheck(receiver, map, true, effect, control, &check_success, | |
| 175 &check_failed); | |
| 176 | |
| 177 // Build the actual load. | |
| 178 Node* load = graph()->NewNode(simplified()->LoadField(field_access), receiver, | |
| 179 effect, check_success); | |
| 180 | |
| 181 // TODO(turbofan): handle slow case instead of deoptimizing. | |
| 182 Node* deopt = graph()->NewNode(common()->Deoptimize(), frame_state_before, | |
| 183 effect, check_failed); | |
| 184 NodeProperties::MergeControlToEnd(graph(), common(), deopt); | |
| 185 ReplaceWithValue(node, load, load, check_success); | |
| 186 return Replace(load); | |
| 187 } | |
| 188 | |
| 189 | |
| 190 Reduction JSTypeFeedbackSpecializer::ReduceJSLoadGlobal(Node* node) { | |
| 191 DCHECK(node->opcode() == IrOpcode::kJSLoadGlobal); | |
| 192 Handle<String> name = | |
| 193 Handle<String>::cast(LoadGlobalParametersOf(node->op()).name()); | |
| 194 if (global_object_.is_null()) { | |
| 195 // Nothing else can be done if we don't have a global object. | |
| 196 return NoChange(); | |
| 197 } | |
| 198 | |
| 199 if (mode() == kDeoptimizationEnabled) { | |
| 200 // Handle lookups in the script context. | |
| 201 { | |
| 202 Handle<ScriptContextTable> script_contexts( | |
| 203 global_object_->native_context()->script_context_table()); | |
| 204 ScriptContextTable::LookupResult lookup; | |
| 205 if (ScriptContextTable::Lookup(script_contexts, name, &lookup)) { | |
| 206 // TODO(turbofan): introduce a LoadContext here. | |
| 207 return NoChange(); | |
| 208 } | |
| 209 } | |
| 210 | |
| 211 // Constant promotion or cell access requires lazy deoptimization support. | |
| 212 LookupIterator it(global_object_, name, LookupIterator::OWN); | |
| 213 | |
| 214 if (it.state() == LookupIterator::DATA) { | |
| 215 Handle<PropertyCell> cell = it.GetPropertyCell(); | |
| 216 dependencies_->AssumePropertyCell(cell); | |
| 217 | |
| 218 if (it.property_details().cell_type() == PropertyCellType::kConstant) { | |
| 219 // Constant promote the global's current value. | |
| 220 Handle<Object> constant_value(cell->value(), jsgraph()->isolate()); | |
| 221 if (constant_value->IsConsString()) { | |
| 222 constant_value = | |
| 223 String::Flatten(Handle<String>::cast(constant_value)); | |
| 224 } | |
| 225 Node* constant = jsgraph()->Constant(constant_value); | |
| 226 ReplaceWithValue(node, constant); | |
| 227 return Replace(constant); | |
| 228 } else { | |
| 229 // Load directly from the property cell. | |
| 230 FieldAccess access = AccessBuilder::ForPropertyCellValue(); | |
| 231 Node* control = NodeProperties::GetControlInput(node); | |
| 232 Node* load_field = graph()->NewNode( | |
| 233 simplified()->LoadField(access), jsgraph()->Constant(cell), | |
| 234 NodeProperties::GetEffectInput(node), control); | |
| 235 ReplaceWithValue(node, load_field, load_field, control); | |
| 236 return Replace(load_field); | |
| 237 } | |
| 238 } | |
| 239 } else { | |
| 240 // TODO(turbofan): non-configurable properties on the global object | |
| 241 // should be loadable through a cell without deoptimization support. | |
| 242 } | |
| 243 | |
| 244 return NoChange(); | |
| 245 } | |
| 246 | |
| 247 | |
| 248 Reduction JSTypeFeedbackSpecializer::ReduceJSLoadProperty(Node* node) { | |
| 249 return NoChange(); | |
| 250 } | |
| 251 | |
| 252 | |
| 253 Reduction JSTypeFeedbackSpecializer::ReduceJSStoreNamed(Node* node) { | |
| 254 DCHECK(node->opcode() == IrOpcode::kJSStoreNamed); | |
| 255 Node* frame_state_before = GetFrameStateBefore(node); | |
| 256 if (frame_state_before == nullptr) return NoChange(); | |
| 257 | |
| 258 NamedAccess const& p = NamedAccessOf(node->op()); | |
| 259 SmallMapList maps; | |
| 260 TypeFeedbackId id = js_type_feedback_->FindTypeFeedbackId(node); | |
| 261 if (id.IsNone() || oracle()->StoreIsUninitialized(id) == UNINITIALIZED) { | |
| 262 // No type feedback ids or the store is uninitialized. | |
| 263 // TODO(titzer): no feedback from vector ICs from stores. | |
| 264 return NoChange(); | |
| 265 } else { | |
| 266 oracle()->AssignmentReceiverTypes(id, p.name(), &maps); | |
| 267 } | |
| 268 | |
| 269 Node* receiver = node->InputAt(0); | |
| 270 Node* effect = NodeProperties::GetEffectInput(node); | |
| 271 | |
| 272 if (maps.length() != 1) return NoChange(); // TODO(turbofan): polymorphism | |
| 273 | |
| 274 if (!ENABLE_FAST_PROPERTY_STORES) return NoChange(); | |
| 275 | |
| 276 Handle<Map> map = maps.first(); | |
| 277 FieldAccess field_access; | |
| 278 if (!GetInObjectFieldAccess(STORE, map, p.name(), &field_access)) { | |
| 279 return NoChange(); | |
| 280 } | |
| 281 | |
| 282 Node* control = NodeProperties::GetControlInput(node); | |
| 283 Node* check_success; | |
| 284 Node* check_failed; | |
| 285 BuildMapCheck(receiver, map, true, effect, control, &check_success, | |
| 286 &check_failed); | |
| 287 | |
| 288 // Build the actual load. | |
| 289 Node* value = node->InputAt(1); | |
| 290 Node* store = graph()->NewNode(simplified()->StoreField(field_access), | |
| 291 receiver, value, effect, check_success); | |
| 292 | |
| 293 // TODO(turbofan): handle slow case instead of deoptimizing. | |
| 294 Node* deopt = graph()->NewNode(common()->Deoptimize(), frame_state_before, | |
| 295 effect, check_failed); | |
| 296 NodeProperties::MergeControlToEnd(graph(), common(), deopt); | |
| 297 ReplaceWithValue(node, store, store, check_success); | |
| 298 return Replace(store); | |
| 299 } | |
| 300 | |
| 301 | |
| 302 Reduction JSTypeFeedbackSpecializer::ReduceJSStoreProperty(Node* node) { | |
| 303 return NoChange(); | |
| 304 } | |
| 305 | |
| 306 | |
| 307 void JSTypeFeedbackSpecializer::BuildMapCheck(Node* receiver, Handle<Map> map, | |
| 308 bool smi_check, Node* effect, | |
| 309 Node* control, Node** success, | |
| 310 Node** fail) { | |
| 311 Node* if_smi = nullptr; | |
| 312 if (smi_check) { | |
| 313 Node* branch_smi = graph()->NewNode( | |
| 314 common()->Branch(BranchHint::kFalse), | |
| 315 graph()->NewNode(simplified()->ObjectIsSmi(), receiver), control); | |
| 316 if_smi = graph()->NewNode(common()->IfTrue(), branch_smi); | |
| 317 control = graph()->NewNode(common()->IfFalse(), branch_smi); | |
| 318 } | |
| 319 | |
| 320 FieldAccess map_access = AccessBuilder::ForMap(); | |
| 321 Node* receiver_map = graph()->NewNode(simplified()->LoadField(map_access), | |
| 322 receiver, effect, control); | |
| 323 Node* map_const = jsgraph_->Constant(map); | |
| 324 Node* cmp = graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()), | |
| 325 receiver_map, map_const); | |
| 326 Node* branch = | |
| 327 graph()->NewNode(common()->Branch(BranchHint::kTrue), cmp, control); | |
| 328 *success = graph()->NewNode(common()->IfTrue(), branch); | |
| 329 *fail = graph()->NewNode(common()->IfFalse(), branch); | |
| 330 | |
| 331 if (if_smi) { | |
| 332 *fail = graph()->NewNode(common()->Merge(2), *fail, if_smi); | |
| 333 } | |
| 334 } | |
| 335 | |
| 336 | |
| 337 // Get the frame state before an operation if it exists and has a valid | |
| 338 // bailout id. | |
| 339 Node* JSTypeFeedbackSpecializer::GetFrameStateBefore(Node* node) { | |
| 340 int count = OperatorProperties::GetFrameStateInputCount(node->op()); | |
| 341 DCHECK_LE(count, 2); | |
| 342 if (count == 2) { | |
| 343 Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); | |
| 344 if (frame_state->opcode() == IrOpcode::kFrameState) { | |
| 345 BailoutId id = OpParameter<FrameStateInfo>(node).bailout_id(); | |
| 346 if (id != BailoutId::None()) return frame_state; | |
| 347 } | |
| 348 } | |
| 349 return nullptr; | |
| 350 } | |
| 351 | |
| 352 } // namespace compiler | |
| 353 } // namespace internal | |
| 354 } // namespace v8 | |
| OLD | NEW |