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