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 35298040 : Reduction JSContextSpecialization::Reduce(Node* node) {
20 35298040 : switch (node->opcode()) {
21 : case IrOpcode::kParameter:
22 2411121 : return ReduceParameter(node);
23 : case IrOpcode::kJSLoadContext:
24 413125 : return ReduceJSLoadContext(node);
25 : case IrOpcode::kJSStoreContext:
26 420223 : return ReduceJSStoreContext(node);
27 : default:
28 : break;
29 : }
30 : return NoChange();
31 : }
32 :
33 2841418 : Reduction JSContextSpecialization::ReduceParameter(Node* node) {
34 : DCHECK_EQ(IrOpcode::kParameter, node->opcode());
35 2411121 : int const index = ParameterIndexOf(node->op());
36 2411122 : if (index == Linkage::kJSCallClosureParamIndex) {
37 : // Constant-fold the function parameter {node}.
38 : Handle<JSFunction> function;
39 456730 : if (closure().ToHandle(&function)) {
40 430297 : Node* value = jsgraph()->HeapConstant(function);
41 : return Replace(value);
42 : }
43 : }
44 : return NoChange();
45 : }
46 :
47 387823 : Reduction JSContextSpecialization::SimplifyJSLoadContext(Node* node,
48 : Node* new_context,
49 : size_t new_depth) {
50 : DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
51 628351 : const ContextAccess& access = ContextAccessOf(node->op());
52 : DCHECK_LE(new_depth, access.depth());
53 :
54 754618 : if (new_depth == access.depth() &&
55 366795 : new_context == NodeProperties::GetContextInput(node)) {
56 : return NoChange();
57 : }
58 :
59 : const Operator* op = jsgraph_->javascript()->LoadContext(
60 240528 : new_depth, access.index(), access.immutable());
61 120264 : NodeProperties::ReplaceContextInput(node, new_context);
62 120264 : NodeProperties::ChangeOp(node, op);
63 : return Changed(node);
64 : }
65 :
66 420223 : Reduction JSContextSpecialization::SimplifyJSStoreContext(Node* node,
67 : Node* new_context,
68 : size_t new_depth) {
69 : DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());
70 423241 : const ContextAccess& access = ContextAccessOf(node->op());
71 : DCHECK_LE(new_depth, access.depth());
72 :
73 839820 : if (new_depth == access.depth() &&
74 419597 : new_context == NodeProperties::GetContextInput(node)) {
75 : return NoChange();
76 : }
77 :
78 : const Operator* op =
79 3018 : jsgraph_->javascript()->StoreContext(new_depth, access.index());
80 3018 : NodeProperties::ReplaceContextInput(node, new_context);
81 3018 : NodeProperties::ChangeOp(node, op);
82 : return Changed(node);
83 : }
84 :
85 : namespace {
86 :
87 288042 : bool IsContextParameter(Node* node) {
88 : DCHECK_EQ(IrOpcode::kParameter, node->opcode());
89 288042 : Node* const start = NodeProperties::GetValueInput(node, 0);
90 : DCHECK_EQ(IrOpcode::kStart, start->opcode());
91 144021 : 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 288042 : 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 833348 : base::Optional<ContextRef> GetSpecializationContext(
103 833348 : JSHeapBroker* broker, Node* node, size_t* distance,
104 : Maybe<OuterContext> maybe_outer) {
105 833348 : switch (node->opcode()) {
106 : case IrOpcode::kHeapConstant: {
107 123413 : HeapObjectRef object(broker, HeapConstantOf(node->op()));
108 246826 : if (object.IsContext()) return object.AsContext();
109 0 : break;
110 : }
111 : case IrOpcode::kParameter: {
112 : OuterContext outer;
113 303950 : if (maybe_outer.To(&outer) && IsContextParameter(node) &&
114 144021 : *distance >= outer.distance) {
115 144021 : *distance -= outer.distance;
116 144021 : return ContextRef(broker, outer.context);
117 : }
118 : break;
119 : }
120 : default:
121 : break;
122 : }
123 565914 : return base::Optional<ContextRef>();
124 : }
125 :
126 : } // anonymous namespace
127 :
128 1064620 : Reduction JSContextSpecialization::ReduceJSLoadContext(Node* node) {
129 : DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
130 :
131 702646 : const ContextAccess& access = ContextAccessOf(node->op());
132 413125 : size_t depth = access.depth();
133 :
134 : // First walk up the context chain in the graph as far as possible.
135 413125 : Node* context = NodeProperties::GetOuterContext(node, &depth);
136 :
137 : base::Optional<ContextRef> maybe_concrete =
138 413125 : GetSpecializationContext(broker(), context, &depth, outer());
139 413125 : 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 149453 : return SimplifyJSLoadContext(node, context, depth);
143 : }
144 :
145 : // Now walk up the concrete context chain for the remaining depth.
146 263672 : ContextRef concrete = maybe_concrete.value();
147 263672 : concrete.Serialize(); // TODO(neis): Remove later.
148 284404 : for (; depth > 0; --depth) {
149 20732 : concrete = concrete.previous();
150 : }
151 :
152 263672 : 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 237823 : return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
156 : }
157 :
158 : // This will hold the final value, if we can figure it out.
159 25849 : base::Optional<ObjectRef> maybe_value;
160 :
161 51698 : maybe_value = concrete.get(static_cast<int>(access.index()));
162 25849 : if (maybe_value.has_value() && !maybe_value->IsSmi()) {
163 : // Even though the context slot is immutable, the context might have escaped
164 : // before the function to which it belongs has initialized the slot.
165 : // We must be conservative and check if the value in the slot is currently
166 : // the hole or undefined. Only if it is neither of these, can we be sure
167 : // that it won't change anymore.
168 23285 : OddballType oddball_type = maybe_value->AsHeapObject().map().oddball_type();
169 46570 : if (oddball_type == OddballType::kUndefined ||
170 23285 : oddball_type == OddballType::kHole) {
171 : maybe_value.reset();
172 : }
173 : }
174 :
175 25849 : if (!maybe_value.has_value()) {
176 1094 : return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
177 : }
178 :
179 : // Success. The context load can be replaced with the constant.
180 : // TODO(titzer): record the specialization for sharing code across
181 : // multiple contexts that have the same value in the corresponding context
182 : // slot.
183 25302 : Node* constant = jsgraph_->Constant(*maybe_value);
184 25302 : ReplaceWithValue(node, constant);
185 : return Replace(constant);
186 : }
187 :
188 :
189 844208 : Reduction JSContextSpecialization::ReduceJSStoreContext(Node* node) {
190 : DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());
191 :
192 420223 : const ContextAccess& access = ContextAccessOf(node->op());
193 420223 : size_t depth = access.depth();
194 :
195 : // First walk up the context chain in the graph until we reduce the depth to 0
196 : // or hit a node that does not have a CreateXYZContext operator.
197 420223 : Node* context = NodeProperties::GetOuterContext(node, &depth);
198 :
199 : base::Optional<ContextRef> maybe_concrete =
200 420223 : GetSpecializationContext(broker(), context, &depth, outer());
201 420223 : if (!maybe_concrete.has_value()) {
202 : // We do not have a concrete context object, so we can only partially reduce
203 : // the load by folding-in the outer context node.
204 416461 : return SimplifyJSStoreContext(node, context, depth);
205 : }
206 :
207 : // Now walk up the concrete context chain for the remaining depth.
208 3762 : ContextRef concrete = maybe_concrete.value();
209 3762 : concrete.Serialize(); // TODO(neis): Remove later.
210 4220 : for (; depth > 0; --depth) {
211 458 : concrete = concrete.previous();
212 : }
213 :
214 3762 : return SimplifyJSStoreContext(node, jsgraph()->Constant(concrete), depth);
215 : }
216 :
217 :
218 0 : Isolate* JSContextSpecialization::isolate() const {
219 0 : return jsgraph()->isolate();
220 : }
221 :
222 : } // namespace compiler
223 : } // namespace internal
224 178779 : } // namespace v8
|