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
JSGlobalObjectSpecialization(Editor * editor,JSGraph * jsgraph,Handle<JSGlobalObject> global_object,CompilationDependencies * dependencies)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
Reduce(Node * node)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
ForPropertyCellValue(MachineRepresentation representation,Type * type,Handle<Name> name)51 FieldAccess ForPropertyCellValue(MachineRepresentation representation,
52 Type* type, Handle<Name> name) {
53 WriteBarrierKind kind = kFullWriteBarrier;
54 if (representation == MachineRepresentation::kTaggedSigned) {
55 kind = kNoWriteBarrier;
56 } else if (representation == MachineRepresentation::kTaggedPointer) {
57 kind = kPointerWriteBarrier;
58 }
59 MachineType r = MachineType::TypeForRepresentation(representation);
60 FieldAccess access = {kTaggedBase, PropertyCell::kValueOffset, name, type, r,
61 kind};
62 return access;
63 }
64 } // namespace
65
ReduceJSLoadGlobal(Node * node)66 Reduction JSGlobalObjectSpecialization::ReduceJSLoadGlobal(Node* node) {
67 DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode());
68 Handle<Name> name = LoadGlobalParametersOf(node->op()).name();
69 Node* effect = NodeProperties::GetEffectInput(node);
70 Node* control = NodeProperties::GetControlInput(node);
71
72 // Try to lookup the name on the script context table first (lexical scoping).
73 ScriptContextTableLookupResult result;
74 if (LookupInScriptContextTable(name, &result)) {
75 if (result.context->is_the_hole(isolate(), result.index)) return NoChange();
76 Node* context = jsgraph()->HeapConstant(result.context);
77 Node* value = effect = graph()->NewNode(
78 javascript()->LoadContext(0, result.index, result.immutable), context,
79 context, effect);
80 ReplaceWithValue(node, value, effect);
81 return Replace(value);
82 }
83
84 // Lookup on the global object instead. We only deal with own data
85 // properties of the global object here (represented as PropertyCell).
86 LookupIterator it(global_object(), name, LookupIterator::OWN);
87 if (it.state() != LookupIterator::DATA) return NoChange();
88 if (!it.GetHolder<JSObject>()->IsJSGlobalObject()) return NoChange();
89 Handle<PropertyCell> property_cell = it.GetPropertyCell();
90 PropertyDetails property_details = property_cell->property_details();
91 Handle<Object> property_cell_value(property_cell->value(), isolate());
92
93 // Load from non-configurable, read-only data property on the global
94 // object can be constant-folded, even without deoptimization support.
95 if (!property_details.IsConfigurable() && property_details.IsReadOnly()) {
96 Node* value = jsgraph()->Constant(property_cell_value);
97 ReplaceWithValue(node, value);
98 return Replace(value);
99 }
100
101 // Record a code dependency on the cell if we can benefit from the
102 // additional feedback, or the global property is configurable (i.e.
103 // can be deleted or reconfigured to an accessor property).
104 if (property_details.cell_type() != PropertyCellType::kMutable ||
105 property_details.IsConfigurable()) {
106 dependencies()->AssumePropertyCell(property_cell);
107 }
108
109 // Load from constant/undefined global property can be constant-folded.
110 if (property_details.cell_type() == PropertyCellType::kConstant ||
111 property_details.cell_type() == PropertyCellType::kUndefined) {
112 Node* value = jsgraph()->Constant(property_cell_value);
113 ReplaceWithValue(node, value);
114 return Replace(value);
115 }
116
117 // Load from constant type cell can benefit from type feedback.
118 Type* property_cell_value_type = Type::NonInternal();
119 MachineRepresentation representation = MachineRepresentation::kTagged;
120 if (property_details.cell_type() == PropertyCellType::kConstantType) {
121 // Compute proper type based on the current value in the cell.
122 if (property_cell_value->IsSmi()) {
123 property_cell_value_type = Type::SignedSmall();
124 representation = MachineRepresentation::kTaggedSigned;
125 } else if (property_cell_value->IsNumber()) {
126 property_cell_value_type = Type::Number();
127 representation = MachineRepresentation::kTaggedPointer;
128 } else {
129 // TODO(turbofan): Track the property_cell_value_map on the FieldAccess
130 // below and use it in LoadElimination to eliminate map checks.
131 Handle<Map> property_cell_value_map(
132 Handle<HeapObject>::cast(property_cell_value)->map(), isolate());
133 property_cell_value_type = Type::For(property_cell_value_map);
134 representation = MachineRepresentation::kTaggedPointer;
135 }
136 }
137 Node* value = effect =
138 graph()->NewNode(simplified()->LoadField(ForPropertyCellValue(
139 representation, property_cell_value_type, name)),
140 jsgraph()->HeapConstant(property_cell), effect, control);
141 ReplaceWithValue(node, value, effect, control);
142 return Replace(value);
143 }
144
145
ReduceJSStoreGlobal(Node * node)146 Reduction JSGlobalObjectSpecialization::ReduceJSStoreGlobal(Node* node) {
147 DCHECK_EQ(IrOpcode::kJSStoreGlobal, node->opcode());
148 Handle<Name> name = StoreGlobalParametersOf(node->op()).name();
149 Node* value = NodeProperties::GetValueInput(node, 0);
150 Node* effect = NodeProperties::GetEffectInput(node);
151 Node* control = NodeProperties::GetControlInput(node);
152
153 // Try to lookup the name on the script context table first (lexical scoping).
154 ScriptContextTableLookupResult result;
155 if (LookupInScriptContextTable(name, &result)) {
156 if (result.context->is_the_hole(isolate(), result.index)) return NoChange();
157 if (result.immutable) return NoChange();
158 Node* context = jsgraph()->HeapConstant(result.context);
159 effect = graph()->NewNode(javascript()->StoreContext(0, result.index),
160 context, value, context, effect, control);
161 ReplaceWithValue(node, value, effect, control);
162 return Replace(value);
163 }
164
165 // Lookup on the global object instead. We only deal with own data
166 // properties of the global object here (represented as PropertyCell).
167 LookupIterator it(global_object(), name, LookupIterator::OWN);
168 if (it.state() != LookupIterator::DATA) return NoChange();
169 if (!it.GetHolder<JSObject>()->IsJSGlobalObject()) 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 // Record a code dependency on the cell, and just deoptimize if the new
182 // value doesn't match the previous value stored inside the cell.
183 dependencies()->AssumePropertyCell(property_cell);
184 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), value,
185 jsgraph()->Constant(property_cell_value));
186 effect =
187 graph()->NewNode(simplified()->CheckIf(), check, effect, control);
188 break;
189 }
190 case PropertyCellType::kConstantType: {
191 // Record a code dependency on the cell, and just deoptimize if the new
192 // values' type doesn't match the type of the previous value in the cell.
193 dependencies()->AssumePropertyCell(property_cell);
194 Type* property_cell_value_type;
195 MachineRepresentation representation = MachineRepresentation::kTagged;
196 if (property_cell_value->IsHeapObject()) {
197 // We cannot do anything if the {property_cell_value}s map is no
198 // longer stable.
199 Handle<Map> property_cell_value_map(
200 Handle<HeapObject>::cast(property_cell_value)->map(), isolate());
201 if (!property_cell_value_map->is_stable()) return NoChange();
202 dependencies()->AssumeMapStable(property_cell_value_map);
203
204 // Check that the {value} is a HeapObject.
205 value = effect = graph()->NewNode(simplified()->CheckHeapObject(),
206 value, effect, control);
207
208 // Check {value} map agains the {property_cell} map.
209 effect = graph()->NewNode(
210 simplified()->CheckMaps(1), value,
211 jsgraph()->HeapConstant(property_cell_value_map), effect, control);
212 property_cell_value_type = Type::OtherInternal();
213 representation = MachineRepresentation::kTaggedPointer;
214 } else {
215 // Check that the {value} is a Smi.
216 value = effect =
217 graph()->NewNode(simplified()->CheckSmi(), value, effect, control);
218 property_cell_value_type = Type::SignedSmall();
219 representation = MachineRepresentation::kTaggedSigned;
220 }
221 effect = graph()->NewNode(
222 simplified()->StoreField(ForPropertyCellValue(
223 representation, property_cell_value_type, name)),
224 jsgraph()->HeapConstant(property_cell), value, effect, control);
225 break;
226 }
227 case PropertyCellType::kMutable: {
228 // Store to non-configurable, data property on the global can be lowered
229 // to a field store, even without recording a code dependency on the cell,
230 // because the property cannot be deleted or reconfigured to an accessor
231 // or interceptor property.
232 if (property_details.IsConfigurable()) {
233 // Protect lowering by recording a code dependency on the cell.
234 dependencies()->AssumePropertyCell(property_cell);
235 }
236 effect = graph()->NewNode(
237 simplified()->StoreField(ForPropertyCellValue(
238 MachineRepresentation::kTagged, Type::NonInternal(), name)),
239 jsgraph()->HeapConstant(property_cell), value, effect, control);
240 break;
241 }
242 }
243 ReplaceWithValue(node, value, effect, control);
244 return Replace(value);
245 }
246
LookupInScriptContextTable(Handle<Name> name,ScriptContextTableLookupResult * result)247 bool JSGlobalObjectSpecialization::LookupInScriptContextTable(
248 Handle<Name> name, ScriptContextTableLookupResult* result) {
249 if (!name->IsString()) return false;
250 Handle<ScriptContextTable> script_context_table(
251 global_object()->native_context()->script_context_table(), isolate());
252 ScriptContextTable::LookupResult lookup_result;
253 if (!ScriptContextTable::Lookup(script_context_table,
254 Handle<String>::cast(name), &lookup_result)) {
255 return false;
256 }
257 Handle<Context> script_context = ScriptContextTable::GetContext(
258 script_context_table, lookup_result.context_index);
259 result->context = script_context;
260 result->immutable = lookup_result.mode == CONST;
261 result->index = lookup_result.slot_index;
262 return true;
263 }
264
graph() const265 Graph* JSGlobalObjectSpecialization::graph() const {
266 return jsgraph()->graph();
267 }
268
isolate() const269 Isolate* JSGlobalObjectSpecialization::isolate() const {
270 return jsgraph()->isolate();
271 }
272
common() const273 CommonOperatorBuilder* JSGlobalObjectSpecialization::common() const {
274 return jsgraph()->common();
275 }
276
javascript() const277 JSOperatorBuilder* JSGlobalObjectSpecialization::javascript() const {
278 return jsgraph()->javascript();
279 }
280
simplified() const281 SimplifiedOperatorBuilder* JSGlobalObjectSpecialization::simplified() const {
282 return jsgraph()->simplified();
283 }
284
285 } // namespace compiler
286 } // namespace internal
287 } // namespace v8
288