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