blob: 132dec6ffb2c84c6ab3fb72edcbcf24ea75344c6 [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001// 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
18namespace v8 {
19namespace internal {
20namespace compiler {
21
22struct JSGlobalObjectSpecialization::ScriptContextTableLookupResult {
23 Handle<Context> context;
24 bool immutable;
25 int index;
26};
27
28
29JSGlobalObjectSpecialization::JSGlobalObjectSpecialization(
Ben Murdoch097c5b22016-05-18 11:27:45 +010030 Editor* editor, JSGraph* jsgraph,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000031 MaybeHandle<Context> native_context, CompilationDependencies* dependencies)
32 : AdvancedReducer(editor),
33 jsgraph_(jsgraph),
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000034 native_context_(native_context),
35 dependencies_(dependencies),
36 type_cache_(TypeCache::Get()) {}
37
38
39Reduction JSGlobalObjectSpecialization::Reduce(Node* node) {
40 switch (node->opcode()) {
41 case IrOpcode::kJSLoadGlobal:
42 return ReduceJSLoadGlobal(node);
43 case IrOpcode::kJSStoreGlobal:
44 return ReduceJSStoreGlobal(node);
45 default:
46 break;
47 }
48 return NoChange();
49}
50
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000051Reduction JSGlobalObjectSpecialization::ReduceJSLoadGlobal(Node* node) {
52 DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode());
53 Handle<Name> name = LoadGlobalParametersOf(node->op()).name();
54 Node* effect = NodeProperties::GetEffectInput(node);
55 Node* control = NodeProperties::GetControlInput(node);
56
57 // Retrieve the global object from the given {node}.
58 Handle<JSGlobalObject> global_object;
59 if (!GetGlobalObject(node).ToHandle(&global_object)) return NoChange();
60
61 // Try to lookup the name on the script context table first (lexical scoping).
62 ScriptContextTableLookupResult result;
63 if (LookupInScriptContextTable(global_object, name, &result)) {
64 if (result.context->is_the_hole(result.index)) return NoChange();
65 Node* context = jsgraph()->HeapConstant(result.context);
66 Node* value = effect = graph()->NewNode(
67 javascript()->LoadContext(0, result.index, result.immutable), context,
68 context, effect);
69 ReplaceWithValue(node, value, effect);
70 return Replace(value);
71 }
72
73 // Lookup on the global object instead. We only deal with own data
74 // properties of the global object here (represented as PropertyCell).
75 LookupIterator it(global_object, name, LookupIterator::OWN);
76 if (it.state() != LookupIterator::DATA) return NoChange();
77 Handle<PropertyCell> property_cell = it.GetPropertyCell();
78 PropertyDetails property_details = property_cell->property_details();
79 Handle<Object> property_cell_value(property_cell->value(), isolate());
80
81 // Load from non-configurable, read-only data property on the global
82 // object can be constant-folded, even without deoptimization support.
83 if (!property_details.IsConfigurable() && property_details.IsReadOnly()) {
84 Node* value = jsgraph()->Constant(property_cell_value);
85 ReplaceWithValue(node, value);
86 return Replace(value);
87 }
88
Ben Murdoch097c5b22016-05-18 11:27:45 +010089 // Record a code dependency on the cell if we can benefit from the
90 // additional feedback, or the global property is configurable (i.e.
91 // can be deleted or reconfigured to an accessor property).
92 if (property_details.cell_type() != PropertyCellType::kMutable ||
93 property_details.IsConfigurable()) {
94 dependencies()->AssumePropertyCell(property_cell);
95 }
96
97 // Load from constant/undefined global property can be constant-folded.
98 if (property_details.cell_type() == PropertyCellType::kConstant ||
99 property_details.cell_type() == PropertyCellType::kUndefined) {
100 Node* value = jsgraph()->Constant(property_cell_value);
101 ReplaceWithValue(node, value);
102 return Replace(value);
103 }
104
105 // Load from constant type cell can benefit from type feedback.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000106 Type* property_cell_value_type = Type::Tagged();
Ben Murdoch097c5b22016-05-18 11:27:45 +0100107 if (property_details.cell_type() == PropertyCellType::kConstantType) {
108 // Compute proper type based on the current value in the cell.
109 if (property_cell_value->IsSmi()) {
110 property_cell_value_type = type_cache_.kSmi;
111 } else if (property_cell_value->IsNumber()) {
112 property_cell_value_type = type_cache_.kHeapNumber;
113 } else {
114 Handle<Map> property_cell_value_map(
115 Handle<HeapObject>::cast(property_cell_value)->map(), isolate());
116 property_cell_value_type =
117 Type::Class(property_cell_value_map, graph()->zone());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000118 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000119 }
120 Node* value = effect = graph()->NewNode(
121 simplified()->LoadField(
122 AccessBuilder::ForPropertyCellValue(property_cell_value_type)),
123 jsgraph()->HeapConstant(property_cell), effect, control);
124 ReplaceWithValue(node, value, effect, control);
125 return Replace(value);
126}
127
128
129Reduction JSGlobalObjectSpecialization::ReduceJSStoreGlobal(Node* node) {
130 DCHECK_EQ(IrOpcode::kJSStoreGlobal, node->opcode());
131 Handle<Name> name = StoreGlobalParametersOf(node->op()).name();
132 Node* value = NodeProperties::GetValueInput(node, 0);
133 Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
134 Node* effect = NodeProperties::GetEffectInput(node);
135 Node* control = NodeProperties::GetControlInput(node);
136
137 // Retrieve the global object from the given {node}.
138 Handle<JSGlobalObject> global_object;
139 if (!GetGlobalObject(node).ToHandle(&global_object)) return NoChange();
140
141 // Try to lookup the name on the script context table first (lexical scoping).
142 ScriptContextTableLookupResult result;
143 if (LookupInScriptContextTable(global_object, name, &result)) {
144 if (result.context->is_the_hole(result.index)) return NoChange();
145 if (result.immutable) return NoChange();
146 Node* context = jsgraph()->HeapConstant(result.context);
147 effect = graph()->NewNode(javascript()->StoreContext(0, result.index),
148 context, value, context, effect, control);
149 ReplaceWithValue(node, value, effect, control);
150 return Replace(value);
151 }
152
153 // Lookup on the global object instead. We only deal with own data
154 // properties of the global object here (represented as PropertyCell).
155 LookupIterator it(global_object, name, LookupIterator::OWN);
156 if (it.state() != LookupIterator::DATA) return NoChange();
157 Handle<PropertyCell> property_cell = it.GetPropertyCell();
158 PropertyDetails property_details = property_cell->property_details();
159 Handle<Object> property_cell_value(property_cell->value(), isolate());
160
161 // Don't even bother trying to lower stores to read-only data properties.
162 if (property_details.IsReadOnly()) return NoChange();
163 switch (property_details.cell_type()) {
164 case PropertyCellType::kUndefined: {
165 return NoChange();
166 }
167 case PropertyCellType::kConstant: {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100168 // Record a code dependency on the cell, and just deoptimize if the new
169 // value doesn't match the previous value stored inside the cell.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000170 dependencies()->AssumePropertyCell(property_cell);
171 Node* check =
172 graph()->NewNode(simplified()->ReferenceEqual(Type::Tagged()), value,
173 jsgraph()->Constant(property_cell_value));
174 Node* branch =
175 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
176 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
177 Node* deoptimize =
178 graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kEager),
179 frame_state, effect, if_false);
180 // TODO(bmeurer): This should be on the AdvancedReducer somehow.
181 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100182 Revisit(graph()->end());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000183 control = graph()->NewNode(common()->IfTrue(), branch);
184 break;
185 }
186 case PropertyCellType::kConstantType: {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100187 // Record a code dependency on the cell, and just deoptimize if the new
188 // values' type doesn't match the type of the previous value in the cell.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000189 dependencies()->AssumePropertyCell(property_cell);
190 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value);
191 Type* property_cell_value_type = Type::TaggedSigned();
192 if (property_cell_value->IsHeapObject()) {
193 // Deoptimize if the {value} is a Smi.
194 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse),
195 check, control);
196 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
197 Node* deoptimize =
198 graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kEager),
199 frame_state, effect, if_true);
200 // TODO(bmeurer): This should be on the AdvancedReducer somehow.
201 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100202 Revisit(graph()->end());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000203 control = graph()->NewNode(common()->IfFalse(), branch);
204
205 // Load the {value} map check against the {property_cell} map.
206 Node* value_map = effect =
207 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
208 value, effect, control);
209 Handle<Map> property_cell_value_map(
210 Handle<HeapObject>::cast(property_cell_value)->map(), isolate());
211 check = graph()->NewNode(
212 simplified()->ReferenceEqual(Type::Any()), value_map,
213 jsgraph()->HeapConstant(property_cell_value_map));
214 property_cell_value_type = Type::TaggedPointer();
215 }
216 Node* branch =
217 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
218 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
219 Node* deoptimize =
220 graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kEager),
221 frame_state, effect, if_false);
222 // TODO(bmeurer): This should be on the AdvancedReducer somehow.
223 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100224 Revisit(graph()->end());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000225 control = graph()->NewNode(common()->IfTrue(), branch);
226 effect = graph()->NewNode(
227 simplified()->StoreField(
228 AccessBuilder::ForPropertyCellValue(property_cell_value_type)),
229 jsgraph()->HeapConstant(property_cell), value, effect, control);
230 break;
231 }
232 case PropertyCellType::kMutable: {
233 // Store to non-configurable, data property on the global can be lowered
Ben Murdoch097c5b22016-05-18 11:27:45 +0100234 // to a field store, even without recording a code dependency on the cell,
235 // because the property cannot be deleted or reconfigured to an accessor
236 // or interceptor property.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000237 if (property_details.IsConfigurable()) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100238 // Protect lowering by recording a code dependency on the cell.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000239 dependencies()->AssumePropertyCell(property_cell);
240 }
241 effect = graph()->NewNode(
242 simplified()->StoreField(AccessBuilder::ForPropertyCellValue()),
243 jsgraph()->HeapConstant(property_cell), value, effect, control);
244 break;
245 }
246 }
247 ReplaceWithValue(node, value, effect, control);
248 return Replace(value);
249}
250
251
252MaybeHandle<JSGlobalObject> JSGlobalObjectSpecialization::GetGlobalObject(
253 Node* node) {
254 Node* const context = NodeProperties::GetContextInput(node);
255 return NodeProperties::GetSpecializationGlobalObject(context,
256 native_context());
257}
258
259
260bool JSGlobalObjectSpecialization::LookupInScriptContextTable(
261 Handle<JSGlobalObject> global_object, Handle<Name> name,
262 ScriptContextTableLookupResult* result) {
263 if (!name->IsString()) return false;
264 Handle<ScriptContextTable> script_context_table(
265 global_object->native_context()->script_context_table(), isolate());
266 ScriptContextTable::LookupResult lookup_result;
267 if (!ScriptContextTable::Lookup(script_context_table,
268 Handle<String>::cast(name), &lookup_result)) {
269 return false;
270 }
271 Handle<Context> script_context = ScriptContextTable::GetContext(
272 script_context_table, lookup_result.context_index);
273 result->context = script_context;
274 result->immutable = IsImmutableVariableMode(lookup_result.mode);
275 result->index = lookup_result.slot_index;
276 return true;
277}
278
279
280Graph* JSGlobalObjectSpecialization::graph() const {
281 return jsgraph()->graph();
282}
283
284
285Isolate* JSGlobalObjectSpecialization::isolate() const {
286 return jsgraph()->isolate();
287}
288
289
290CommonOperatorBuilder* JSGlobalObjectSpecialization::common() const {
291 return jsgraph()->common();
292}
293
294
295JSOperatorBuilder* JSGlobalObjectSpecialization::javascript() const {
296 return jsgraph()->javascript();
297}
298
299
300SimplifiedOperatorBuilder* JSGlobalObjectSpecialization::simplified() const {
301 return jsgraph()->simplified();
302}
303
304} // namespace compiler
305} // namespace internal
306} // namespace v8