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/lookup.h"
15 #include "src/objects-inl.h" // TODO(mstarzinger): Temporary cycle breaker!
16 #include "src/type-cache.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(Editor * editor,JSGraph * jsgraph,Flags flags,MaybeHandle<Context> native_context,CompilationDependencies * dependencies)29 JSGlobalObjectSpecialization::JSGlobalObjectSpecialization(
30 Editor* editor, JSGraph* jsgraph, Flags flags,
31 MaybeHandle<Context> native_context, CompilationDependencies* dependencies)
32 : AdvancedReducer(editor),
33 jsgraph_(jsgraph),
34 flags_(flags),
35 native_context_(native_context),
36 dependencies_(dependencies),
37 type_cache_(TypeCache::Get()) {}
38
39
Reduce(Node * node)40 Reduction JSGlobalObjectSpecialization::Reduce(Node* node) {
41 switch (node->opcode()) {
42 case IrOpcode::kJSLoadGlobal:
43 return ReduceJSLoadGlobal(node);
44 case IrOpcode::kJSStoreGlobal:
45 return ReduceJSStoreGlobal(node);
46 default:
47 break;
48 }
49 return NoChange();
50 }
51
52
ReduceJSLoadGlobal(Node * node)53 Reduction JSGlobalObjectSpecialization::ReduceJSLoadGlobal(Node* node) {
54 DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode());
55 Handle<Name> name = LoadGlobalParametersOf(node->op()).name();
56 Node* effect = NodeProperties::GetEffectInput(node);
57 Node* control = NodeProperties::GetControlInput(node);
58
59 // Retrieve the global object from the given {node}.
60 Handle<JSGlobalObject> global_object;
61 if (!GetGlobalObject(node).ToHandle(&global_object)) return NoChange();
62
63 // Try to lookup the name on the script context table first (lexical scoping).
64 ScriptContextTableLookupResult result;
65 if (LookupInScriptContextTable(global_object, name, &result)) {
66 if (result.context->is_the_hole(result.index)) return NoChange();
67 Node* context = jsgraph()->HeapConstant(result.context);
68 Node* value = effect = graph()->NewNode(
69 javascript()->LoadContext(0, result.index, result.immutable), context,
70 context, effect);
71 ReplaceWithValue(node, value, effect);
72 return Replace(value);
73 }
74
75 // Lookup on the global object instead. We only deal with own data
76 // properties of the global object here (represented as PropertyCell).
77 LookupIterator it(global_object, name, LookupIterator::OWN);
78 if (it.state() != LookupIterator::DATA) return NoChange();
79 Handle<PropertyCell> property_cell = it.GetPropertyCell();
80 PropertyDetails property_details = property_cell->property_details();
81 Handle<Object> property_cell_value(property_cell->value(), isolate());
82
83 // Load from non-configurable, read-only data property on the global
84 // object can be constant-folded, even without deoptimization support.
85 if (!property_details.IsConfigurable() && property_details.IsReadOnly()) {
86 Node* value = jsgraph()->Constant(property_cell_value);
87 ReplaceWithValue(node, value);
88 return Replace(value);
89 }
90
91 // Load from non-configurable, data property on the global can be lowered to
92 // a field load, even without deoptimization, because the property cannot be
93 // deleted or reconfigured to an accessor/interceptor property. Yet, if
94 // deoptimization support is available, we can constant-fold certain global
95 // properties or at least lower them to field loads annotated with more
96 // precise type feedback.
97 Type* property_cell_value_type = Type::Tagged();
98 if (flags() & kDeoptimizationEnabled) {
99 // Record a code dependency on the cell if we can benefit from the
100 // additional feedback, or the global property is configurable (i.e.
101 // can be deleted or reconfigured to an accessor property).
102 if (property_details.cell_type() != PropertyCellType::kMutable ||
103 property_details.IsConfigurable()) {
104 dependencies()->AssumePropertyCell(property_cell);
105 }
106
107 // Load from constant/undefined global property can be constant-folded.
108 if ((property_details.cell_type() == PropertyCellType::kConstant ||
109 property_details.cell_type() == PropertyCellType::kUndefined)) {
110 Node* value = jsgraph()->Constant(property_cell_value);
111 ReplaceWithValue(node, value);
112 return Replace(value);
113 }
114
115 // Load from constant type cell can benefit from type feedback.
116 if (property_details.cell_type() == PropertyCellType::kConstantType) {
117 // Compute proper type based on the current value in the cell.
118 if (property_cell_value->IsSmi()) {
119 property_cell_value_type = type_cache_.kSmi;
120 } else if (property_cell_value->IsNumber()) {
121 property_cell_value_type = type_cache_.kHeapNumber;
122 } else {
123 Handle<Map> property_cell_value_map(
124 Handle<HeapObject>::cast(property_cell_value)->map(), isolate());
125 property_cell_value_type =
126 Type::Class(property_cell_value_map, graph()->zone());
127 }
128 }
129 } else if (property_details.IsConfigurable()) {
130 // Access to configurable global properties requires deoptimization support.
131 return NoChange();
132 }
133 Node* value = effect = graph()->NewNode(
134 simplified()->LoadField(
135 AccessBuilder::ForPropertyCellValue(property_cell_value_type)),
136 jsgraph()->HeapConstant(property_cell), effect, control);
137 ReplaceWithValue(node, value, effect, control);
138 return Replace(value);
139 }
140
141
ReduceJSStoreGlobal(Node * node)142 Reduction JSGlobalObjectSpecialization::ReduceJSStoreGlobal(Node* node) {
143 DCHECK_EQ(IrOpcode::kJSStoreGlobal, node->opcode());
144 Handle<Name> name = StoreGlobalParametersOf(node->op()).name();
145 Node* value = NodeProperties::GetValueInput(node, 0);
146 Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
147 Node* effect = NodeProperties::GetEffectInput(node);
148 Node* control = NodeProperties::GetControlInput(node);
149
150 // Retrieve the global object from the given {node}.
151 Handle<JSGlobalObject> global_object;
152 if (!GetGlobalObject(node).ToHandle(&global_object)) return NoChange();
153
154 // Try to lookup the name on the script context table first (lexical scoping).
155 ScriptContextTableLookupResult result;
156 if (LookupInScriptContextTable(global_object, name, &result)) {
157 if (result.context->is_the_hole(result.index)) return NoChange();
158 if (result.immutable) return NoChange();
159 Node* context = jsgraph()->HeapConstant(result.context);
160 effect = graph()->NewNode(javascript()->StoreContext(0, result.index),
161 context, value, context, effect, control);
162 ReplaceWithValue(node, value, effect, control);
163 return Replace(value);
164 }
165
166 // Lookup on the global object instead. We only deal with own data
167 // properties of the global object here (represented as PropertyCell).
168 LookupIterator it(global_object, name, LookupIterator::OWN);
169 if (it.state() != LookupIterator::DATA) return NoChange();
170 Handle<PropertyCell> property_cell = it.GetPropertyCell();
171 PropertyDetails property_details = property_cell->property_details();
172 Handle<Object> property_cell_value(property_cell->value(), isolate());
173
174 // Don't even bother trying to lower stores to read-only data properties.
175 if (property_details.IsReadOnly()) return NoChange();
176 switch (property_details.cell_type()) {
177 case PropertyCellType::kUndefined: {
178 return NoChange();
179 }
180 case PropertyCellType::kConstant: {
181 // Store to constant property cell requires deoptimization support,
182 // because we might even need to eager deoptimize for mismatch.
183 if (!(flags() & kDeoptimizationEnabled)) return NoChange();
184 dependencies()->AssumePropertyCell(property_cell);
185 Node* check =
186 graph()->NewNode(simplified()->ReferenceEqual(Type::Tagged()), value,
187 jsgraph()->Constant(property_cell_value));
188 Node* branch =
189 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
190 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
191 Node* deoptimize =
192 graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kEager),
193 frame_state, effect, if_false);
194 // TODO(bmeurer): This should be on the AdvancedReducer somehow.
195 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
196 control = graph()->NewNode(common()->IfTrue(), branch);
197 break;
198 }
199 case PropertyCellType::kConstantType: {
200 // Store to constant-type property cell requires deoptimization support,
201 // because we might even need to eager deoptimize for mismatch.
202 if (!(flags() & kDeoptimizationEnabled)) return NoChange();
203 dependencies()->AssumePropertyCell(property_cell);
204 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value);
205 Type* property_cell_value_type = Type::TaggedSigned();
206 if (property_cell_value->IsHeapObject()) {
207 // Deoptimize if the {value} is a Smi.
208 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse),
209 check, control);
210 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
211 Node* deoptimize =
212 graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kEager),
213 frame_state, effect, if_true);
214 // TODO(bmeurer): This should be on the AdvancedReducer somehow.
215 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
216 control = graph()->NewNode(common()->IfFalse(), branch);
217
218 // Load the {value} map check against the {property_cell} map.
219 Node* value_map = effect =
220 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
221 value, effect, control);
222 Handle<Map> property_cell_value_map(
223 Handle<HeapObject>::cast(property_cell_value)->map(), isolate());
224 check = graph()->NewNode(
225 simplified()->ReferenceEqual(Type::Any()), value_map,
226 jsgraph()->HeapConstant(property_cell_value_map));
227 property_cell_value_type = Type::TaggedPointer();
228 }
229 Node* branch =
230 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
231 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
232 Node* deoptimize =
233 graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kEager),
234 frame_state, effect, if_false);
235 // TODO(bmeurer): This should be on the AdvancedReducer somehow.
236 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
237 control = graph()->NewNode(common()->IfTrue(), branch);
238 effect = graph()->NewNode(
239 simplified()->StoreField(
240 AccessBuilder::ForPropertyCellValue(property_cell_value_type)),
241 jsgraph()->HeapConstant(property_cell), value, effect, control);
242 break;
243 }
244 case PropertyCellType::kMutable: {
245 // Store to non-configurable, data property on the global can be lowered
246 // to a field store, even without deoptimization, because the property
247 // cannot be deleted or reconfigured to an accessor/interceptor property.
248 if (property_details.IsConfigurable()) {
249 // With deoptimization support, we can lower stores even to configurable
250 // data properties on the global object, by adding a code dependency on
251 // the cell.
252 if (!(flags() & kDeoptimizationEnabled)) return NoChange();
253 dependencies()->AssumePropertyCell(property_cell);
254 }
255 effect = graph()->NewNode(
256 simplified()->StoreField(AccessBuilder::ForPropertyCellValue()),
257 jsgraph()->HeapConstant(property_cell), value, effect, control);
258 break;
259 }
260 }
261 ReplaceWithValue(node, value, effect, control);
262 return Replace(value);
263 }
264
265
GetGlobalObject(Node * node)266 MaybeHandle<JSGlobalObject> JSGlobalObjectSpecialization::GetGlobalObject(
267 Node* node) {
268 Node* const context = NodeProperties::GetContextInput(node);
269 return NodeProperties::GetSpecializationGlobalObject(context,
270 native_context());
271 }
272
273
LookupInScriptContextTable(Handle<JSGlobalObject> global_object,Handle<Name> name,ScriptContextTableLookupResult * result)274 bool JSGlobalObjectSpecialization::LookupInScriptContextTable(
275 Handle<JSGlobalObject> global_object, Handle<Name> name,
276 ScriptContextTableLookupResult* result) {
277 if (!name->IsString()) return false;
278 Handle<ScriptContextTable> script_context_table(
279 global_object->native_context()->script_context_table(), isolate());
280 ScriptContextTable::LookupResult lookup_result;
281 if (!ScriptContextTable::Lookup(script_context_table,
282 Handle<String>::cast(name), &lookup_result)) {
283 return false;
284 }
285 Handle<Context> script_context = ScriptContextTable::GetContext(
286 script_context_table, lookup_result.context_index);
287 result->context = script_context;
288 result->immutable = IsImmutableVariableMode(lookup_result.mode);
289 result->index = lookup_result.slot_index;
290 return true;
291 }
292
293
graph() const294 Graph* JSGlobalObjectSpecialization::graph() const {
295 return jsgraph()->graph();
296 }
297
298
isolate() const299 Isolate* JSGlobalObjectSpecialization::isolate() const {
300 return jsgraph()->isolate();
301 }
302
303
common() const304 CommonOperatorBuilder* JSGlobalObjectSpecialization::common() const {
305 return jsgraph()->common();
306 }
307
308
javascript() const309 JSOperatorBuilder* JSGlobalObjectSpecialization::javascript() const {
310 return jsgraph()->javascript();
311 }
312
313
simplified() const314 SimplifiedOperatorBuilder* JSGlobalObjectSpecialization::simplified() const {
315 return jsgraph()->simplified();
316 }
317
318 } // namespace compiler
319 } // namespace internal
320 } // namespace v8
321