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/type-info.h" |
| 12 |
| 13 #include "src/compiler/access-builder.h" |
| 14 #include "src/compiler/common-operator.h" |
| 15 #include "src/compiler/node-aux-data.h" |
| 16 #include "src/compiler/simplified-operator.h" |
| 17 |
| 18 namespace v8 { |
| 19 namespace internal { |
| 20 namespace compiler { |
| 21 |
| 22 enum LoadOrStore { LOAD, STORE }; |
| 23 |
| 24 JSTypeFeedbackTable::JSTypeFeedbackTable(Zone* zone) |
| 25 : map_(TypeFeedbackIdMap::key_compare(), |
| 26 TypeFeedbackIdMap::allocator_type(zone)) {} |
| 27 |
| 28 |
| 29 void JSTypeFeedbackTable::Record(Node* node, TypeFeedbackId id) { |
| 30 map_.insert(std::make_pair(node->id(), id)); |
| 31 } |
| 32 |
| 33 |
| 34 Reduction JSTypeFeedbackSpecializer::Reduce(Node* node) { |
| 35 // TODO(turbofan): type feedback currently requires deoptimization. |
| 36 if (!FLAG_turbo_deoptimization) return NoChange(); |
| 37 switch (node->opcode()) { |
| 38 case IrOpcode::kJSLoadProperty: |
| 39 return ReduceJSLoadProperty(node); |
| 40 case IrOpcode::kJSLoadNamed: |
| 41 return ReduceJSLoadNamed(node); |
| 42 case IrOpcode::kJSStoreNamed: |
| 43 return ReduceJSStoreNamed(node); |
| 44 case IrOpcode::kJSStoreProperty: |
| 45 return ReduceJSStoreProperty(node); |
| 46 default: |
| 47 break; |
| 48 } |
| 49 return NoChange(); |
| 50 } |
| 51 |
| 52 |
| 53 static bool GetInObjectFieldAccess(LoadOrStore mode, Handle<Map> map, |
| 54 Handle<Name> name, FieldAccess* access) { |
| 55 access->base_is_tagged = kTaggedBase; |
| 56 access->offset = -1; |
| 57 access->name = name; |
| 58 access->type = Type::Any(); |
| 59 access->machine_type = kMachAnyTagged; |
| 60 |
| 61 // Check for properties that have accessors but are JSObject fields. |
| 62 if (Accessors::IsJSObjectFieldAccessor(map, name, &access->offset)) { |
| 63 // TODO(turbofan): fill in types for special JSObject field accesses. |
| 64 return true; |
| 65 } |
| 66 |
| 67 // Check if the map is a dictionary. |
| 68 if (map->is_dictionary_map()) return false; |
| 69 |
| 70 // Search the descriptor array. |
| 71 DescriptorArray* descriptors = map->instance_descriptors(); |
| 72 int number = descriptors->SearchWithCache(*name, *map); |
| 73 if (number == DescriptorArray::kNotFound) return false; |
| 74 PropertyDetails property_details = descriptors->GetDetails(number); |
| 75 |
| 76 bool is_smi = property_details.representation().IsSmi(); |
| 77 bool is_double = property_details.representation().IsDouble(); |
| 78 |
| 79 if (property_details.type() != DATA) { |
| 80 // TODO(turbofan): constant loads and stores. |
| 81 return false; |
| 82 } |
| 83 |
| 84 if (mode == STORE) { |
| 85 if (property_details.IsReadOnly()) return false; |
| 86 if (is_smi) { |
| 87 // TODO(turbofan): SMI stores. |
| 88 return false; |
| 89 } |
| 90 if (is_double) { |
| 91 // TODO(turbofan): double stores. |
| 92 return false; |
| 93 } |
| 94 } else { |
| 95 // Check property details for loads. |
| 96 if (is_smi) { |
| 97 access->type = Type::SignedSmall(); |
| 98 access->machine_type = static_cast<MachineType>(kTypeInt32 | kRepTagged); |
| 99 } |
| 100 if (is_double) { |
| 101 access->type = Type::Number(); |
| 102 access->machine_type = kMachFloat64; |
| 103 } |
| 104 } |
| 105 |
| 106 int index = map->instance_descriptors()->GetFieldIndex(number); |
| 107 FieldIndex field_index = FieldIndex::ForPropertyIndex(*map, index, is_double); |
| 108 |
| 109 if (field_index.is_inobject()) { |
| 110 access->offset = field_index.offset(); |
| 111 return true; |
| 112 } |
| 113 |
| 114 // TODO(turbofan): handle out of object properties. |
| 115 return false; |
| 116 } |
| 117 |
| 118 |
| 119 Reduction JSTypeFeedbackSpecializer::ReduceJSLoadNamed(Node* node) { |
| 120 DCHECK(node->opcode() == IrOpcode::kJSLoadNamed); |
| 121 TypeFeedbackId id = js_type_feedback_->find(node); |
| 122 if (id.IsNone() || oracle()->LoadIsUninitialized(id)) return NoChange(); |
| 123 |
| 124 const LoadNamedParameters& p = LoadNamedParametersOf(node->op()); |
| 125 SmallMapList maps; |
| 126 Handle<Name> name = p.name().handle(); |
| 127 Node* receiver = node->InputAt(0); |
| 128 Node* effect = NodeProperties::GetEffectInput(node); |
| 129 GatherReceiverTypes(receiver, effect, id, name, &maps); |
| 130 |
| 131 if (maps.length() != 1) return NoChange(); // TODO(turbofan): polymorphism |
| 132 |
| 133 Handle<Map> map = maps.first(); |
| 134 FieldAccess field_access; |
| 135 if (!GetInObjectFieldAccess(LOAD, map, name, &field_access)) { |
| 136 return NoChange(); |
| 137 } |
| 138 |
| 139 Node* control = NodeProperties::GetControlInput(node); |
| 140 Node* check_success; |
| 141 Node* check_failed; |
| 142 BuildMapCheck(receiver, map, true, effect, control, &check_success, |
| 143 &check_failed); |
| 144 |
| 145 // Build the actual load. |
| 146 Node* load = graph()->NewNode(simplified()->LoadField(field_access), receiver, |
| 147 effect, check_success); |
| 148 |
| 149 // TODO(turbofan): handle slow case instead of deoptimizing. |
| 150 // TODO(titzer): frame state should be from before the load. |
| 151 Node* frame_state = NodeProperties::GetFrameStateInput(node, 0); |
| 152 Node* deopt = graph()->NewNode(common()->Deoptimize(), frame_state, effect, |
| 153 check_failed); |
| 154 NodeProperties::MergeControlToEnd(graph(), common(), deopt); |
| 155 NodeProperties::ReplaceWithValue(node, load, load, check_success); |
| 156 return Replace(load); |
| 157 } |
| 158 |
| 159 |
| 160 Reduction JSTypeFeedbackSpecializer::ReduceJSLoadProperty(Node* node) { |
| 161 return NoChange(); |
| 162 } |
| 163 |
| 164 |
| 165 Reduction JSTypeFeedbackSpecializer::ReduceJSStoreNamed(Node* node) { |
| 166 DCHECK(node->opcode() == IrOpcode::kJSStoreNamed); |
| 167 TypeFeedbackId id = js_type_feedback_->find(node); |
| 168 if (id.IsNone() || oracle()->StoreIsUninitialized(id)) return NoChange(); |
| 169 |
| 170 const StoreNamedParameters& p = StoreNamedParametersOf(node->op()); |
| 171 SmallMapList maps; |
| 172 Handle<Name> name = p.name().handle(); |
| 173 Node* receiver = node->InputAt(0); |
| 174 Node* effect = NodeProperties::GetEffectInput(node); |
| 175 GatherReceiverTypes(receiver, effect, id, name, &maps); |
| 176 |
| 177 if (maps.length() != 1) return NoChange(); // TODO(turbofan): polymorphism |
| 178 |
| 179 Handle<Map> map = maps.first(); |
| 180 FieldAccess field_access; |
| 181 if (!GetInObjectFieldAccess(STORE, map, name, &field_access)) { |
| 182 return NoChange(); |
| 183 } |
| 184 |
| 185 Node* control = NodeProperties::GetControlInput(node); |
| 186 Node* check_success; |
| 187 Node* check_failed; |
| 188 BuildMapCheck(receiver, map, true, effect, control, &check_success, |
| 189 &check_failed); |
| 190 |
| 191 // Build the actual load. |
| 192 Node* value = node->InputAt(1); |
| 193 Node* store = graph()->NewNode(simplified()->StoreField(field_access), |
| 194 receiver, value, effect, check_success); |
| 195 |
| 196 // TODO(turbofan): handle slow case instead of deoptimizing. |
| 197 // TODO(titzer): frame state should be from before the store. |
| 198 Node* frame_state = NodeProperties::GetFrameStateInput(node, 0); |
| 199 Node* deopt = graph()->NewNode(common()->Deoptimize(), frame_state, effect, |
| 200 check_failed); |
| 201 NodeProperties::MergeControlToEnd(graph(), common(), deopt); |
| 202 NodeProperties::ReplaceWithValue(node, store, store, check_success); |
| 203 return Replace(store); |
| 204 } |
| 205 |
| 206 |
| 207 Reduction JSTypeFeedbackSpecializer::ReduceJSStoreProperty(Node* node) { |
| 208 return NoChange(); |
| 209 } |
| 210 |
| 211 |
| 212 void JSTypeFeedbackSpecializer::BuildMapCheck(Node* receiver, Handle<Map> map, |
| 213 bool smi_check, Node* effect, |
| 214 Node* control, Node** success, |
| 215 Node** fail) { |
| 216 Node* if_smi = nullptr; |
| 217 if (smi_check) { |
| 218 Node* branch_smi = graph()->NewNode( |
| 219 common()->Branch(BranchHint::kFalse), |
| 220 graph()->NewNode(simplified()->ObjectIsSmi(), receiver), control); |
| 221 if_smi = graph()->NewNode(common()->IfTrue(), branch_smi); |
| 222 control = graph()->NewNode(common()->IfFalse(), branch_smi); |
| 223 } |
| 224 |
| 225 FieldAccess map_access = AccessBuilder::ForMap(); |
| 226 Node* receiver_map = graph()->NewNode(simplified()->LoadField(map_access), |
| 227 receiver, effect, control); |
| 228 Node* map_const = jsgraph_->Constant(map); |
| 229 Node* cmp = graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()), |
| 230 receiver_map, map_const); |
| 231 Node* branch = |
| 232 graph()->NewNode(common()->Branch(BranchHint::kTrue), cmp, control); |
| 233 *success = graph()->NewNode(common()->IfTrue(), branch); |
| 234 *fail = graph()->NewNode(common()->IfFalse(), branch); |
| 235 |
| 236 if (if_smi) { |
| 237 *fail = graph()->NewNode(common()->Merge(2), *fail, if_smi); |
| 238 } |
| 239 } |
| 240 |
| 241 |
| 242 void JSTypeFeedbackSpecializer::GatherReceiverTypes(Node* receiver, |
| 243 Node* effect, |
| 244 TypeFeedbackId id, |
| 245 Handle<Name> name, |
| 246 SmallMapList* maps) { |
| 247 // TODO(turbofan): filter maps by initial receiver map if known |
| 248 // TODO(turbofan): filter maps by native context (if specializing) |
| 249 // TODO(turbofan): filter maps by effect chain |
| 250 oracle()->PropertyReceiverTypes(id, name, maps); |
| 251 } |
| 252 |
| 253 |
| 254 } // namespace compiler |
| 255 } // namespace internal |
| 256 } // namespace v8 |
OLD | NEW |