Line data Source code
1 : // Copyright 2014 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-context-specialization.h"
6 :
7 : #include "src/compiler/common-operator.h"
8 : #include "src/compiler/js-graph.h"
9 : #include "src/compiler/js-operator.h"
10 : #include "src/compiler/linkage.h"
11 : #include "src/compiler/node-matchers.h"
12 : #include "src/compiler/node-properties.h"
13 : #include "src/contexts-inl.h"
14 :
15 : namespace v8 {
16 : namespace internal {
17 : namespace compiler {
18 :
19 34781845 : Reduction JSContextSpecialization::Reduce(Node* node) {
20 34781845 : switch (node->opcode()) {
21 : case IrOpcode::kParameter:
22 2437599 : return ReduceParameter(node);
23 : case IrOpcode::kJSLoadContext:
24 422235 : return ReduceJSLoadContext(node);
25 : case IrOpcode::kJSStoreContext:
26 443987 : return ReduceJSStoreContext(node);
27 : default:
28 : break;
29 : }
30 : return NoChange();
31 : }
32 :
33 2437600 : Reduction JSContextSpecialization::ReduceParameter(Node* node) {
34 : DCHECK_EQ(IrOpcode::kParameter, node->opcode());
35 2437600 : int const index = ParameterIndexOf(node->op());
36 2437596 : if (index == Linkage::kJSCallClosureParamIndex) {
37 : // Constant-fold the function parameter {node}.
38 : Handle<JSFunction> function;
39 463876 : if (closure().ToHandle(&function)) {
40 435902 : Node* value = jsgraph()->HeapConstant(function);
41 : return Replace(value);
42 : }
43 : }
44 : return NoChange();
45 : }
46 :
47 396323 : Reduction JSContextSpecialization::SimplifyJSLoadContext(Node* node,
48 : Node* new_context,
49 : size_t new_depth) {
50 : DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
51 396323 : const ContextAccess& access = ContextAccessOf(node->op());
52 : DCHECK_LE(new_depth, access.depth());
53 :
54 770193 : if (new_depth == access.depth() &&
55 373870 : new_context == NodeProperties::GetContextInput(node)) {
56 : return NoChange();
57 : }
58 :
59 245540 : const Operator* op = jsgraph_->javascript()->LoadContext(
60 122770 : new_depth, access.index(), access.immutable());
61 122770 : NodeProperties::ReplaceContextInput(node, new_context);
62 122770 : NodeProperties::ChangeOp(node, op);
63 : return Changed(node);
64 : }
65 :
66 443987 : Reduction JSContextSpecialization::SimplifyJSStoreContext(Node* node,
67 : Node* new_context,
68 : size_t new_depth) {
69 : DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());
70 443987 : const ContextAccess& access = ContextAccessOf(node->op());
71 : DCHECK_LE(new_depth, access.depth());
72 :
73 887346 : if (new_depth == access.depth() &&
74 443359 : new_context == NodeProperties::GetContextInput(node)) {
75 : return NoChange();
76 : }
77 :
78 : const Operator* op =
79 2982 : jsgraph_->javascript()->StoreContext(new_depth, access.index());
80 2982 : NodeProperties::ReplaceContextInput(node, new_context);
81 2982 : NodeProperties::ChangeOp(node, op);
82 : return Changed(node);
83 : }
84 :
85 : namespace {
86 :
87 147668 : bool IsContextParameter(Node* node) {
88 : DCHECK_EQ(IrOpcode::kParameter, node->opcode());
89 147668 : Node* const start = NodeProperties::GetValueInput(node, 0);
90 : DCHECK_EQ(IrOpcode::kStart, start->opcode());
91 147668 : int const index = ParameterIndexOf(node->op());
92 : // The context is always the last parameter to a JavaScript function, and
93 : // {Parameter} indices start at -1, so value outputs of {Start} look like
94 : // this: closure, receiver, param0, ..., paramN, context.
95 147668 : return index == start->op()->ValueOutputCount() - 2;
96 : }
97 :
98 : // Given a context {node} and the {distance} from that context to the target
99 : // context (which we want to read from or store to), try to return a
100 : // specialization context. If successful, update {distance} to whatever
101 : // distance remains from the specialization context.
102 866222 : base::Optional<ContextRef> GetSpecializationContext(
103 : JSHeapBroker* broker, Node* node, size_t* distance,
104 : Maybe<OuterContext> maybe_outer) {
105 866222 : switch (node->opcode()) {
106 : case IrOpcode::kHeapConstant: {
107 123495 : HeapObjectRef object(broker, HeapConstantOf(node->op()));
108 246990 : if (object.IsContext()) return object.AsContext();
109 0 : break;
110 : }
111 : case IrOpcode::kParameter: {
112 : OuterContext outer;
113 311311 : if (maybe_outer.To(&outer) && IsContextParameter(node) &&
114 147668 : *distance >= outer.distance) {
115 147668 : *distance -= outer.distance;
116 147668 : return ContextRef(broker, outer.context);
117 : }
118 : break;
119 : }
120 : default:
121 : break;
122 : }
123 595059 : return base::Optional<ContextRef>();
124 : }
125 :
126 : } // anonymous namespace
127 :
128 422235 : Reduction JSContextSpecialization::ReduceJSLoadContext(Node* node) {
129 : DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
130 :
131 422235 : const ContextAccess& access = ContextAccessOf(node->op());
132 422235 : size_t depth = access.depth();
133 :
134 : // First walk up the context chain in the graph as far as possible.
135 422235 : Node* context = NodeProperties::GetOuterContext(node, &depth);
136 :
137 : base::Optional<ContextRef> maybe_concrete =
138 422235 : GetSpecializationContext(broker(), context, &depth, outer());
139 422235 : if (!maybe_concrete.has_value()) {
140 : // We do not have a concrete context object, so we can only partially reduce
141 : // the load by folding-in the outer context node.
142 154899 : return SimplifyJSLoadContext(node, context, depth);
143 : }
144 :
145 : // Now walk up the concrete context chain for the remaining depth.
146 267336 : ContextRef concrete = maybe_concrete.value();
147 267336 : concrete.SerializeContextChain(); // TODO(neis): Remove later.
148 312216 : for (; depth > 0; --depth) {
149 22440 : concrete = concrete.previous();
150 : }
151 :
152 267336 : if (!access.immutable()) {
153 : // We found the requested context object but since the context slot is
154 : // mutable we can only partially reduce the load.
155 240873 : return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
156 : }
157 :
158 : // This will hold the final value, if we can figure it out.
159 26463 : base::Optional<ObjectRef> maybe_value;
160 :
161 26463 : concrete.SerializeSlot(static_cast<int>(access.index()));
162 52926 : maybe_value = concrete.get(static_cast<int>(access.index()));
163 26463 : if (maybe_value.has_value() && !maybe_value->IsSmi()) {
164 : // Even though the context slot is immutable, the context might have escaped
165 : // before the function to which it belongs has initialized the slot.
166 : // We must be conservative and check if the value in the slot is currently
167 : // the hole or undefined. Only if it is neither of these, can we be sure
168 : // that it won't change anymore.
169 23891 : OddballType oddball_type = maybe_value->AsHeapObject().map().oddball_type();
170 47782 : if (oddball_type == OddballType::kUndefined ||
171 23891 : oddball_type == OddballType::kHole) {
172 : maybe_value.reset();
173 : }
174 : }
175 :
176 26463 : if (!maybe_value.has_value()) {
177 551 : return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
178 : }
179 :
180 : // Success. The context load can be replaced with the constant.
181 : // TODO(titzer): record the specialization for sharing code across
182 : // multiple contexts that have the same value in the corresponding context
183 : // slot.
184 25912 : Node* constant = jsgraph_->Constant(*maybe_value);
185 : ReplaceWithValue(node, constant);
186 : return Replace(constant);
187 : }
188 :
189 :
190 443987 : Reduction JSContextSpecialization::ReduceJSStoreContext(Node* node) {
191 : DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());
192 :
193 443987 : const ContextAccess& access = ContextAccessOf(node->op());
194 443987 : size_t depth = access.depth();
195 :
196 : // First walk up the context chain in the graph until we reduce the depth to 0
197 : // or hit a node that does not have a CreateXYZContext operator.
198 443987 : Node* context = NodeProperties::GetOuterContext(node, &depth);
199 :
200 : base::Optional<ContextRef> maybe_concrete =
201 443987 : GetSpecializationContext(broker(), context, &depth, outer());
202 443987 : if (!maybe_concrete.has_value()) {
203 : // We do not have a concrete context object, so we can only partially reduce
204 : // the load by folding-in the outer context node.
205 440160 : return SimplifyJSStoreContext(node, context, depth);
206 : }
207 :
208 : // Now walk up the concrete context chain for the remaining depth.
209 3827 : ContextRef concrete = maybe_concrete.value();
210 3827 : concrete.SerializeContextChain(); // TODO(neis): Remove later.
211 4747 : for (; depth > 0; --depth) {
212 460 : concrete = concrete.previous();
213 : }
214 :
215 3827 : return SimplifyJSStoreContext(node, jsgraph()->Constant(concrete), depth);
216 : }
217 :
218 :
219 0 : Isolate* JSContextSpecialization::isolate() const {
220 0 : return jsgraph()->isolate();
221 : }
222 :
223 : } // namespace compiler
224 : } // namespace internal
225 120216 : } // namespace v8
|