| OLD | NEW |
| 1 // Copyright 2015 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/compiler/js-type-feedback.h" | 5 #include "src/compiler/js-type-feedback.h" |
| 6 | 6 |
| 7 #include "src/property-details.h" | 7 #include "src/property-details.h" |
| 8 | 8 |
| 9 #include "src/accessors.h" | 9 #include "src/accessors.h" |
| 10 #include "src/ast.h" | 10 #include "src/ast.h" |
| 11 #include "src/compiler.h" |
| 11 #include "src/type-info.h" | 12 #include "src/type-info.h" |
| 12 | 13 |
| 13 #include "src/compiler/access-builder.h" | 14 #include "src/compiler/access-builder.h" |
| 14 #include "src/compiler/common-operator.h" | 15 #include "src/compiler/common-operator.h" |
| 15 #include "src/compiler/node-aux-data.h" | 16 #include "src/compiler/node-aux-data.h" |
| 16 #include "src/compiler/node-matchers.h" | 17 #include "src/compiler/node-matchers.h" |
| 17 #include "src/compiler/operator-properties.h" | 18 #include "src/compiler/operator-properties.h" |
| 18 #include "src/compiler/simplified-operator.h" | 19 #include "src/compiler/simplified-operator.h" |
| 19 | 20 |
| 20 namespace v8 { | 21 namespace v8 { |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 71 } | 72 } |
| 72 return ReduceJSStoreProperty(node); | 73 return ReduceJSStoreProperty(node); |
| 73 } | 74 } |
| 74 default: | 75 default: |
| 75 break; | 76 break; |
| 76 } | 77 } |
| 77 return NoChange(); | 78 return NoChange(); |
| 78 } | 79 } |
| 79 | 80 |
| 80 | 81 |
| 82 static void AddFieldAccessTypes(FieldAccess* access, |
| 83 PropertyDetails property_details) { |
| 84 if (property_details.representation().IsSmi()) { |
| 85 access->type = Type::SignedSmall(); |
| 86 access->machine_type = static_cast<MachineType>(kTypeInt32 | kRepTagged); |
| 87 } else if (property_details.representation().IsDouble()) { |
| 88 access->type = Type::Number(); |
| 89 access->machine_type = kMachFloat64; |
| 90 } |
| 91 } |
| 92 |
| 93 |
| 81 static bool GetInObjectFieldAccess(LoadOrStore mode, Handle<Map> map, | 94 static bool GetInObjectFieldAccess(LoadOrStore mode, Handle<Map> map, |
| 82 Handle<Name> name, FieldAccess* access) { | 95 Handle<Name> name, FieldAccess* access) { |
| 83 access->base_is_tagged = kTaggedBase; | 96 access->base_is_tagged = kTaggedBase; |
| 84 access->offset = -1; | 97 access->offset = -1; |
| 85 access->name = name; | 98 access->name = name; |
| 86 access->type = Type::Any(); | 99 access->type = Type::Any(); |
| 87 access->machine_type = kMachAnyTagged; | 100 access->machine_type = kMachAnyTagged; |
| 88 | 101 |
| 89 // Check for properties that have accessors but are JSObject fields. | 102 // Check for properties that have accessors but are JSObject fields. |
| 90 if (Accessors::IsJSObjectFieldAccessor(map, name, &access->offset)) { | 103 if (Accessors::IsJSObjectFieldAccessor(map, name, &access->offset)) { |
| (...skipping 11 matching lines...) Expand all Loading... |
| 102 PropertyDetails property_details = descriptors->GetDetails(number); | 115 PropertyDetails property_details = descriptors->GetDetails(number); |
| 103 | 116 |
| 104 bool is_smi = property_details.representation().IsSmi(); | 117 bool is_smi = property_details.representation().IsSmi(); |
| 105 bool is_double = property_details.representation().IsDouble(); | 118 bool is_double = property_details.representation().IsDouble(); |
| 106 | 119 |
| 107 if (property_details.type() != DATA) { | 120 if (property_details.type() != DATA) { |
| 108 // TODO(turbofan): constant loads and stores. | 121 // TODO(turbofan): constant loads and stores. |
| 109 return false; | 122 return false; |
| 110 } | 123 } |
| 111 | 124 |
| 125 // Transfer known types from property details. |
| 126 AddFieldAccessTypes(access, property_details); |
| 127 |
| 112 if (mode == STORE) { | 128 if (mode == STORE) { |
| 113 if (property_details.IsReadOnly()) return false; | 129 if (property_details.IsReadOnly()) { |
| 114 if (is_smi) { | 130 // TODO(turbofan): deopt, ignore or throw on readonly stores. |
| 115 // TODO(turbofan): SMI stores. | |
| 116 return false; | 131 return false; |
| 117 } | 132 } |
| 118 if (is_double) { | 133 if (is_smi || is_double) { |
| 119 // TODO(turbofan): double stores. | 134 // TODO(turbofan): check type and deopt for SMI/double stores. |
| 120 return false; | 135 return false; |
| 121 } | 136 } |
| 122 } else { | |
| 123 // Check property details for loads. | |
| 124 if (is_smi) { | |
| 125 access->type = Type::SignedSmall(); | |
| 126 access->machine_type = static_cast<MachineType>(kTypeInt32 | kRepTagged); | |
| 127 } | |
| 128 if (is_double) { | |
| 129 access->type = Type::Number(); | |
| 130 access->machine_type = kMachFloat64; | |
| 131 } | |
| 132 } | 137 } |
| 133 | 138 |
| 134 int index = map->instance_descriptors()->GetFieldIndex(number); | 139 int index = map->instance_descriptors()->GetFieldIndex(number); |
| 135 FieldIndex field_index = FieldIndex::ForPropertyIndex(*map, index, is_double); | 140 FieldIndex field_index = FieldIndex::ForPropertyIndex(*map, index, is_double); |
| 136 | 141 |
| 137 if (field_index.is_inobject()) { | 142 if (field_index.is_inobject()) { |
| 138 access->offset = field_index.offset(); | 143 access->offset = field_index.offset(); |
| 139 return true; | 144 return true; |
| 140 } | 145 } |
| 141 | 146 |
| 142 // TODO(turbofan): handle out of object properties. | 147 // TODO(turbofan): handle out of object properties. |
| 143 return false; | 148 return false; |
| 144 } | 149 } |
| 145 | 150 |
| 146 | 151 |
| 152 static bool IsGlobalObject(Node* node) { |
| 153 return NodeProperties::IsTyped(node) && |
| 154 NodeProperties::GetBounds(node).upper->Is(Type::GlobalObject()); |
| 155 } |
| 156 |
| 157 |
| 147 Reduction JSTypeFeedbackSpecializer::ReduceJSLoadNamed(Node* node) { | 158 Reduction JSTypeFeedbackSpecializer::ReduceJSLoadNamed(Node* node) { |
| 148 DCHECK(node->opcode() == IrOpcode::kJSLoadNamed); | 159 DCHECK(node->opcode() == IrOpcode::kJSLoadNamed); |
| 160 Node* receiver = node->InputAt(0); |
| 161 if (IsGlobalObject(receiver)) { |
| 162 return ReduceJSLoadNamedForGlobalVariable(node); |
| 163 } |
| 164 |
| 165 if (!FLAG_turbo_deoptimization) return NoChange(); |
| 149 // TODO(titzer): deopt locations are wrong for property accesses | 166 // TODO(titzer): deopt locations are wrong for property accesses |
| 150 if (!EAGER_DEOPT_LOCATIONS_FOR_PROPERTY_ACCESS_ARE_CORRECT) return NoChange(); | 167 if (!EAGER_DEOPT_LOCATIONS_FOR_PROPERTY_ACCESS_ARE_CORRECT) return NoChange(); |
| 151 | 168 |
| 152 // TODO(turbofan): handle vector-based type feedback. | 169 // TODO(turbofan): handle vector-based type feedback. |
| 153 TypeFeedbackId id = js_type_feedback_->find(node); | 170 TypeFeedbackId id = js_type_feedback_->find(node); |
| 154 if (id.IsNone() || oracle()->LoadInlineCacheState(id) == UNINITIALIZED) { | 171 if (id.IsNone() || oracle()->LoadInlineCacheState(id) == UNINITIALIZED) { |
| 155 return NoChange(); | 172 return NoChange(); |
| 156 } | 173 } |
| 157 | 174 |
| 158 const LoadNamedParameters& p = LoadNamedParametersOf(node->op()); | 175 const LoadNamedParameters& p = LoadNamedParametersOf(node->op()); |
| 159 SmallMapList maps; | 176 SmallMapList maps; |
| 160 Handle<Name> name = p.name().handle(); | 177 Handle<Name> name = p.name().handle(); |
| 161 Node* receiver = node->InputAt(0); | |
| 162 Node* effect = NodeProperties::GetEffectInput(node); | 178 Node* effect = NodeProperties::GetEffectInput(node); |
| 163 GatherReceiverTypes(receiver, effect, id, name, &maps); | 179 GatherReceiverTypes(receiver, effect, id, name, &maps); |
| 164 | 180 |
| 165 if (maps.length() != 1) return NoChange(); // TODO(turbofan): polymorphism | 181 if (maps.length() != 1) return NoChange(); // TODO(turbofan): polymorphism |
| 166 | 182 |
| 167 Handle<Map> map = maps.first(); | 183 Handle<Map> map = maps.first(); |
| 168 FieldAccess field_access; | 184 FieldAccess field_access; |
| 169 if (!GetInObjectFieldAccess(LOAD, map, name, &field_access)) { | 185 if (!GetInObjectFieldAccess(LOAD, map, name, &field_access)) { |
| 170 return NoChange(); | 186 return NoChange(); |
| 171 } | 187 } |
| (...skipping 12 matching lines...) Expand all Loading... |
| 184 // TODO(titzer): frame state should be from before the load. | 200 // TODO(titzer): frame state should be from before the load. |
| 185 Node* frame_state = NodeProperties::GetFrameStateInput(node, 0); | 201 Node* frame_state = NodeProperties::GetFrameStateInput(node, 0); |
| 186 Node* deopt = graph()->NewNode(common()->Deoptimize(), frame_state, effect, | 202 Node* deopt = graph()->NewNode(common()->Deoptimize(), frame_state, effect, |
| 187 check_failed); | 203 check_failed); |
| 188 NodeProperties::MergeControlToEnd(graph(), common(), deopt); | 204 NodeProperties::MergeControlToEnd(graph(), common(), deopt); |
| 189 NodeProperties::ReplaceWithValue(node, load, load, check_success); | 205 NodeProperties::ReplaceWithValue(node, load, load, check_success); |
| 190 return Replace(load); | 206 return Replace(load); |
| 191 } | 207 } |
| 192 | 208 |
| 193 | 209 |
| 210 Reduction JSTypeFeedbackSpecializer::ReduceJSLoadNamedForGlobalVariable( |
| 211 Node* node) { |
| 212 Handle<String> name = |
| 213 Handle<String>::cast(LoadNamedParametersOf(node->op()).name().handle()); |
| 214 // Try to optimize loads from the global object. |
| 215 Handle<Object> constant_value = |
| 216 jsgraph()->isolate()->factory()->GlobalConstantFor(name); |
| 217 if (!constant_value.is_null()) { |
| 218 // Always optimize global constants. |
| 219 Node* constant = jsgraph()->Constant(constant_value); |
| 220 NodeProperties::ReplaceWithValue(node, constant); |
| 221 return Replace(constant); |
| 222 } |
| 223 |
| 224 if (global_object_.is_null()) { |
| 225 // Nothing else can be done if we don't have a global object. |
| 226 return NoChange(); |
| 227 } |
| 228 |
| 229 if (FLAG_turbo_deoptimization) { |
| 230 // Handle lookups in the script context. |
| 231 { |
| 232 Handle<ScriptContextTable> script_contexts( |
| 233 global_object_->native_context()->script_context_table()); |
| 234 ScriptContextTable::LookupResult lookup; |
| 235 if (ScriptContextTable::Lookup(script_contexts, name, &lookup)) { |
| 236 // TODO(turbofan): introduce a LoadContext here. |
| 237 return NoChange(); |
| 238 } |
| 239 } |
| 240 |
| 241 // Constant promotion or cell access requires lazy deoptimization support. |
| 242 LookupIterator it(global_object_, name, LookupIterator::OWN); |
| 243 |
| 244 if (it.state() == LookupIterator::DATA) { |
| 245 Handle<PropertyCell> cell = it.GetPropertyCell(); |
| 246 dependencies_->AssumePropertyCell(cell); |
| 247 |
| 248 if (it.property_details().cell_type() == PropertyCellType::kConstant) { |
| 249 // Constant promote the global's current value. |
| 250 Handle<Object> constant_value(cell->value(), jsgraph()->isolate()); |
| 251 if (constant_value->IsConsString()) { |
| 252 constant_value = |
| 253 String::Flatten(Handle<String>::cast(constant_value)); |
| 254 } |
| 255 Node* constant = jsgraph()->Constant(constant_value); |
| 256 NodeProperties::ReplaceWithValue(node, constant); |
| 257 return Replace(constant); |
| 258 } else { |
| 259 // Load directly from the property cell. |
| 260 FieldAccess access = AccessBuilder::ForPropertyCellValue(); |
| 261 Node* control = NodeProperties::GetControlInput(node); |
| 262 Node* load_field = graph()->NewNode( |
| 263 simplified()->LoadField(access), jsgraph()->Constant(cell), |
| 264 NodeProperties::GetEffectInput(node), control); |
| 265 NodeProperties::ReplaceWithValue(node, load_field, load_field, control); |
| 266 return Replace(load_field); |
| 267 } |
| 268 } |
| 269 } else { |
| 270 // TODO(turbofan): non-configurable properties on the global object |
| 271 // should be loadable through a cell without deoptimization support. |
| 272 } |
| 273 |
| 274 return NoChange(); |
| 275 } |
| 276 |
| 277 |
| 194 Reduction JSTypeFeedbackSpecializer::ReduceJSLoadProperty(Node* node) { | 278 Reduction JSTypeFeedbackSpecializer::ReduceJSLoadProperty(Node* node) { |
| 195 return NoChange(); | 279 return NoChange(); |
| 196 } | 280 } |
| 197 | 281 |
| 198 | 282 |
| 199 Reduction JSTypeFeedbackSpecializer::ReduceJSStoreNamed(Node* node) { | 283 Reduction JSTypeFeedbackSpecializer::ReduceJSStoreNamed(Node* node) { |
| 200 DCHECK(node->opcode() == IrOpcode::kJSStoreNamed); | 284 DCHECK(node->opcode() == IrOpcode::kJSStoreNamed); |
| 201 // TODO(titzer): deopt locations are wrong for property accesses | 285 // TODO(titzer): deopt locations are wrong for property accesses |
| 202 if (!EAGER_DEOPT_LOCATIONS_FOR_PROPERTY_ACCESS_ARE_CORRECT) return NoChange(); | 286 if (!EAGER_DEOPT_LOCATIONS_FOR_PROPERTY_ACCESS_ARE_CORRECT) return NoChange(); |
| 203 | 287 |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 284 // TODO(turbofan): filter maps by initial receiver map if known | 368 // TODO(turbofan): filter maps by initial receiver map if known |
| 285 // TODO(turbofan): filter maps by native context (if specializing) | 369 // TODO(turbofan): filter maps by native context (if specializing) |
| 286 // TODO(turbofan): filter maps by effect chain | 370 // TODO(turbofan): filter maps by effect chain |
| 287 oracle()->PropertyReceiverTypes(id, name, maps); | 371 oracle()->PropertyReceiverTypes(id, name, maps); |
| 288 } | 372 } |
| 289 | 373 |
| 290 | 374 |
| 291 } // namespace compiler | 375 } // namespace compiler |
| 292 } // namespace internal | 376 } // namespace internal |
| 293 } // namespace v8 | 377 } // namespace v8 |
| OLD | NEW |