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