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-global-object-specialization.h" | |
6 | |
7 #include "src/compilation-dependencies.h" | |
8 #include "src/compiler/access-builder.h" | |
9 #include "src/compiler/common-operator.h" | |
10 #include "src/compiler/js-graph.h" | |
11 #include "src/compiler/js-operator.h" | |
12 #include "src/compiler/node-properties.h" | |
13 #include "src/compiler/simplified-operator.h" | |
14 #include "src/compiler/type-cache.h" | |
15 #include "src/lookup.h" | |
16 #include "src/objects-inl.h" | |
17 | |
18 namespace v8 { | |
19 namespace internal { | |
20 namespace compiler { | |
21 | |
22 struct JSGlobalObjectSpecialization::ScriptContextTableLookupResult { | |
23 Handle<Context> context; | |
24 bool immutable; | |
25 int index; | |
26 }; | |
27 | |
28 JSGlobalObjectSpecialization::JSGlobalObjectSpecialization( | |
29 Editor* editor, JSGraph* jsgraph, Handle<JSGlobalObject> global_object, | |
30 CompilationDependencies* dependencies) | |
31 : AdvancedReducer(editor), | |
32 jsgraph_(jsgraph), | |
33 global_object_(global_object), | |
34 dependencies_(dependencies), | |
35 type_cache_(TypeCache::Get()) {} | |
36 | |
37 Reduction JSGlobalObjectSpecialization::Reduce(Node* node) { | |
38 switch (node->opcode()) { | |
39 case IrOpcode::kJSLoadGlobal: | |
40 return ReduceJSLoadGlobal(node); | |
41 case IrOpcode::kJSStoreGlobal: | |
42 return ReduceJSStoreGlobal(node); | |
43 default: | |
44 break; | |
45 } | |
46 return NoChange(); | |
47 } | |
48 | |
49 namespace { | |
50 | |
51 FieldAccess ForPropertyCellValue(MachineRepresentation representation, | |
52 Type* type, MaybeHandle<Map> map, | |
53 Handle<Name> name) { | |
54 WriteBarrierKind kind = kFullWriteBarrier; | |
55 if (representation == MachineRepresentation::kTaggedSigned) { | |
56 kind = kNoWriteBarrier; | |
57 } else if (representation == MachineRepresentation::kTaggedPointer) { | |
58 kind = kPointerWriteBarrier; | |
59 } | |
60 MachineType r = MachineType::TypeForRepresentation(representation); | |
61 FieldAccess access = { | |
62 kTaggedBase, PropertyCell::kValueOffset, name, map, type, r, kind}; | |
63 return access; | |
64 } | |
65 | |
66 } // namespace | |
67 | |
68 Reduction JSGlobalObjectSpecialization::ReduceJSLoadGlobal(Node* node) { | |
69 DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode()); | |
70 Handle<Name> name = LoadGlobalParametersOf(node->op()).name(); | |
71 Node* effect = NodeProperties::GetEffectInput(node); | |
72 Node* control = NodeProperties::GetControlInput(node); | |
73 | |
74 // Try to lookup the name on the script context table first (lexical scoping). | |
75 ScriptContextTableLookupResult result; | |
76 if (LookupInScriptContextTable(name, &result)) { | |
77 if (result.context->is_the_hole(isolate(), result.index)) return NoChange(); | |
78 Node* context = jsgraph()->HeapConstant(result.context); | |
79 Node* value = effect = graph()->NewNode( | |
80 javascript()->LoadContext(0, result.index, result.immutable), context, | |
81 effect); | |
82 ReplaceWithValue(node, value, effect); | |
83 return Replace(value); | |
84 } | |
85 | |
86 // Lookup on the global object instead. We only deal with own data | |
87 // properties of the global object here (represented as PropertyCell). | |
88 LookupIterator it(global_object(), name, LookupIterator::OWN); | |
89 it.TryLookupCachedProperty(); | |
90 if (it.state() != LookupIterator::DATA) return NoChange(); | |
91 if (!it.GetHolder<JSObject>()->IsJSGlobalObject()) return NoChange(); | |
92 Handle<PropertyCell> property_cell = it.GetPropertyCell(); | |
93 PropertyDetails property_details = property_cell->property_details(); | |
94 Handle<Object> property_cell_value(property_cell->value(), isolate()); | |
95 | |
96 // Load from non-configurable, read-only data property on the global | |
97 // object can be constant-folded, even without deoptimization support. | |
98 if (!property_details.IsConfigurable() && property_details.IsReadOnly()) { | |
99 Node* value = jsgraph()->Constant(property_cell_value); | |
100 ReplaceWithValue(node, value); | |
101 return Replace(value); | |
102 } | |
103 | |
104 // Record a code dependency on the cell if we can benefit from the | |
105 // additional feedback, or the global property is configurable (i.e. | |
106 // can be deleted or reconfigured to an accessor property). | |
107 if (property_details.cell_type() != PropertyCellType::kMutable || | |
108 property_details.IsConfigurable()) { | |
109 dependencies()->AssumePropertyCell(property_cell); | |
110 } | |
111 | |
112 // Load from constant/undefined global property can be constant-folded. | |
113 if (property_details.cell_type() == PropertyCellType::kConstant || | |
114 property_details.cell_type() == PropertyCellType::kUndefined) { | |
115 Node* value = jsgraph()->Constant(property_cell_value); | |
116 ReplaceWithValue(node, value); | |
117 return Replace(value); | |
118 } | |
119 | |
120 // Load from constant type cell can benefit from type feedback. | |
121 MaybeHandle<Map> map; | |
122 Type* property_cell_value_type = Type::NonInternal(); | |
123 MachineRepresentation representation = MachineRepresentation::kTagged; | |
124 if (property_details.cell_type() == PropertyCellType::kConstantType) { | |
125 // Compute proper type based on the current value in the cell. | |
126 if (property_cell_value->IsSmi()) { | |
127 property_cell_value_type = Type::SignedSmall(); | |
128 representation = MachineRepresentation::kTaggedSigned; | |
129 } else if (property_cell_value->IsNumber()) { | |
130 property_cell_value_type = Type::Number(); | |
131 representation = MachineRepresentation::kTaggedPointer; | |
132 } else { | |
133 Handle<Map> property_cell_value_map( | |
134 Handle<HeapObject>::cast(property_cell_value)->map(), isolate()); | |
135 property_cell_value_type = Type::For(property_cell_value_map); | |
136 representation = MachineRepresentation::kTaggedPointer; | |
137 | |
138 // We can only use the property cell value map for map check elimination | |
139 // if it's stable, i.e. the HeapObject wasn't mutated without the cell | |
140 // state being updated. | |
141 if (property_cell_value_map->is_stable()) { | |
142 dependencies()->AssumeMapStable(property_cell_value_map); | |
143 map = property_cell_value_map; | |
144 } | |
145 } | |
146 } | |
147 Node* value = effect = graph()->NewNode( | |
148 simplified()->LoadField(ForPropertyCellValue( | |
149 representation, property_cell_value_type, map, name)), | |
150 jsgraph()->HeapConstant(property_cell), effect, control); | |
151 ReplaceWithValue(node, value, effect, control); | |
152 return Replace(value); | |
153 } | |
154 | |
155 | |
156 Reduction JSGlobalObjectSpecialization::ReduceJSStoreGlobal(Node* node) { | |
157 DCHECK_EQ(IrOpcode::kJSStoreGlobal, node->opcode()); | |
158 Handle<Name> name = StoreGlobalParametersOf(node->op()).name(); | |
159 Node* value = NodeProperties::GetValueInput(node, 0); | |
160 Node* effect = NodeProperties::GetEffectInput(node); | |
161 Node* control = NodeProperties::GetControlInput(node); | |
162 | |
163 // Try to lookup the name on the script context table first (lexical scoping). | |
164 ScriptContextTableLookupResult result; | |
165 if (LookupInScriptContextTable(name, &result)) { | |
166 if (result.context->is_the_hole(isolate(), result.index)) return NoChange(); | |
167 if (result.immutable) return NoChange(); | |
168 Node* context = jsgraph()->HeapConstant(result.context); | |
169 effect = graph()->NewNode(javascript()->StoreContext(0, result.index), | |
170 value, context, effect, control); | |
171 ReplaceWithValue(node, value, effect, control); | |
172 return Replace(value); | |
173 } | |
174 | |
175 // Lookup on the global object instead. We only deal with own data | |
176 // properties of the global object here (represented as PropertyCell). | |
177 LookupIterator it(global_object(), name, LookupIterator::OWN); | |
178 if (it.state() != LookupIterator::DATA) return NoChange(); | |
179 if (!it.GetHolder<JSObject>()->IsJSGlobalObject()) return NoChange(); | |
180 Handle<PropertyCell> property_cell = it.GetPropertyCell(); | |
181 PropertyDetails property_details = property_cell->property_details(); | |
182 Handle<Object> property_cell_value(property_cell->value(), isolate()); | |
183 | |
184 // Don't even bother trying to lower stores to read-only data properties. | |
185 if (property_details.IsReadOnly()) return NoChange(); | |
186 switch (property_details.cell_type()) { | |
187 case PropertyCellType::kUndefined: { | |
188 return NoChange(); | |
189 } | |
190 case PropertyCellType::kConstant: { | |
191 // Record a code dependency on the cell, and just deoptimize if the new | |
192 // value doesn't match the previous value stored inside the cell. | |
193 dependencies()->AssumePropertyCell(property_cell); | |
194 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), value, | |
195 jsgraph()->Constant(property_cell_value)); | |
196 effect = | |
197 graph()->NewNode(simplified()->CheckIf(), check, effect, control); | |
198 break; | |
199 } | |
200 case PropertyCellType::kConstantType: { | |
201 // Record a code dependency on the cell, and just deoptimize if the new | |
202 // values' type doesn't match the type of the previous value in the cell. | |
203 dependencies()->AssumePropertyCell(property_cell); | |
204 Type* property_cell_value_type; | |
205 MachineRepresentation representation = MachineRepresentation::kTagged; | |
206 if (property_cell_value->IsHeapObject()) { | |
207 // We cannot do anything if the {property_cell_value}s map is no | |
208 // longer stable. | |
209 Handle<Map> property_cell_value_map( | |
210 Handle<HeapObject>::cast(property_cell_value)->map(), isolate()); | |
211 if (!property_cell_value_map->is_stable()) return NoChange(); | |
212 dependencies()->AssumeMapStable(property_cell_value_map); | |
213 | |
214 // Check that the {value} is a HeapObject. | |
215 value = effect = graph()->NewNode(simplified()->CheckHeapObject(), | |
216 value, effect, control); | |
217 | |
218 // Check {value} map agains the {property_cell} map. | |
219 effect = | |
220 graph()->NewNode(simplified()->CheckMaps( | |
221 CheckMapsFlag::kNone, | |
222 ZoneHandleSet<Map>(property_cell_value_map)), | |
223 value, effect, control); | |
224 property_cell_value_type = Type::OtherInternal(); | |
225 representation = MachineRepresentation::kTaggedPointer; | |
226 } else { | |
227 // Check that the {value} is a Smi. | |
228 value = effect = | |
229 graph()->NewNode(simplified()->CheckSmi(), value, effect, control); | |
230 property_cell_value_type = Type::SignedSmall(); | |
231 representation = MachineRepresentation::kTaggedSigned; | |
232 } | |
233 effect = graph()->NewNode(simplified()->StoreField(ForPropertyCellValue( | |
234 representation, property_cell_value_type, | |
235 MaybeHandle<Map>(), name)), | |
236 jsgraph()->HeapConstant(property_cell), value, | |
237 effect, control); | |
238 break; | |
239 } | |
240 case PropertyCellType::kMutable: { | |
241 // Record a code dependency on the cell, and just deoptimize if the | |
242 // property ever becomes read-only. | |
243 dependencies()->AssumePropertyCell(property_cell); | |
244 effect = graph()->NewNode( | |
245 simplified()->StoreField(ForPropertyCellValue( | |
246 MachineRepresentation::kTagged, Type::NonInternal(), | |
247 MaybeHandle<Map>(), name)), | |
248 jsgraph()->HeapConstant(property_cell), value, effect, control); | |
249 break; | |
250 } | |
251 } | |
252 ReplaceWithValue(node, value, effect, control); | |
253 return Replace(value); | |
254 } | |
255 | |
256 bool JSGlobalObjectSpecialization::LookupInScriptContextTable( | |
257 Handle<Name> name, ScriptContextTableLookupResult* result) { | |
258 if (!name->IsString()) return false; | |
259 Handle<ScriptContextTable> script_context_table( | |
260 global_object()->native_context()->script_context_table(), isolate()); | |
261 ScriptContextTable::LookupResult lookup_result; | |
262 if (!ScriptContextTable::Lookup(script_context_table, | |
263 Handle<String>::cast(name), &lookup_result)) { | |
264 return false; | |
265 } | |
266 Handle<Context> script_context = ScriptContextTable::GetContext( | |
267 script_context_table, lookup_result.context_index); | |
268 result->context = script_context; | |
269 result->immutable = lookup_result.mode == CONST; | |
270 result->index = lookup_result.slot_index; | |
271 return true; | |
272 } | |
273 | |
274 Graph* JSGlobalObjectSpecialization::graph() const { | |
275 return jsgraph()->graph(); | |
276 } | |
277 | |
278 Isolate* JSGlobalObjectSpecialization::isolate() const { | |
279 return jsgraph()->isolate(); | |
280 } | |
281 | |
282 CommonOperatorBuilder* JSGlobalObjectSpecialization::common() const { | |
283 return jsgraph()->common(); | |
284 } | |
285 | |
286 JSOperatorBuilder* JSGlobalObjectSpecialization::javascript() const { | |
287 return jsgraph()->javascript(); | |
288 } | |
289 | |
290 SimplifiedOperatorBuilder* JSGlobalObjectSpecialization::simplified() const { | |
291 return jsgraph()->simplified(); | |
292 } | |
293 | |
294 } // namespace compiler | |
295 } // namespace internal | |
296 } // namespace v8 | |
OLD | NEW |