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 34339829 : Reduction JSContextSpecialization::Reduce(Node* node) {
20 34339829 : switch (node->opcode()) {
21 : case IrOpcode::kParameter:
22 2437888 : return ReduceParameter(node);
23 : case IrOpcode::kJSLoadContext:
24 427181 : return ReduceJSLoadContext(node);
25 : case IrOpcode::kJSStoreContext:
26 451907 : return ReduceJSStoreContext(node);
27 : default:
28 : break;
29 : }
30 : return NoChange();
31 : }
32 :
33 2437887 : Reduction JSContextSpecialization::ReduceParameter(Node* node) {
34 : DCHECK_EQ(IrOpcode::kParameter, node->opcode());
35 2437887 : int const index = ParameterIndexOf(node->op());
36 2437889 : if (index == Linkage::kJSCallClosureParamIndex) {
37 : // Constant-fold the function parameter {node}.
38 : Handle<JSFunction> function;
39 464204 : if (closure().ToHandle(&function)) {
40 437653 : Node* value = jsgraph()->HeapConstant(function);
41 : return Replace(value);
42 : }
43 : }
44 : return NoChange();
45 : }
46 :
47 400003 : Reduction JSContextSpecialization::SimplifyJSLoadContext(Node* node,
48 : Node* new_context,
49 : size_t new_depth) {
50 : DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
51 400003 : const ContextAccess& access = ContextAccessOf(node->op());
52 : DCHECK_LE(new_depth, access.depth());
53 :
54 776965 : if (new_depth == access.depth() &&
55 376962 : new_context == NodeProperties::GetContextInput(node)) {
56 : return NoChange();
57 : }
58 :
59 247522 : const Operator* op = jsgraph_->javascript()->LoadContext(
60 123761 : new_depth, access.index(), access.immutable());
61 123761 : NodeProperties::ReplaceContextInput(node, new_context);
62 123761 : NodeProperties::ChangeOp(node, op);
63 : return Changed(node);
64 : }
65 :
66 451907 : Reduction JSContextSpecialization::SimplifyJSStoreContext(Node* node,
67 : Node* new_context,
68 : size_t new_depth) {
69 : DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());
70 451907 : const ContextAccess& access = ContextAccessOf(node->op());
71 : DCHECK_LE(new_depth, access.depth());
72 :
73 903183 : if (new_depth == access.depth() &&
74 451276 : new_context == NodeProperties::GetContextInput(node)) {
75 : return NoChange();
76 : }
77 :
78 : const Operator* op =
79 2993 : jsgraph_->javascript()->StoreContext(new_depth, access.index());
80 2993 : NodeProperties::ReplaceContextInput(node, new_context);
81 2993 : NodeProperties::ChangeOp(node, op);
82 : return Changed(node);
83 : }
84 :
85 : namespace {
86 :
87 149355 : bool IsContextParameter(Node* node) {
88 : DCHECK_EQ(IrOpcode::kParameter, node->opcode());
89 149355 : Node* const start = NodeProperties::GetValueInput(node, 0);
90 : DCHECK_EQ(IrOpcode::kStart, start->opcode());
91 149355 : 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 149355 : 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 879088 : base::Optional<ContextRef> GetSpecializationContext(
103 : JSHeapBroker* broker, Node* node, size_t* distance,
104 : Maybe<OuterContext> maybe_outer) {
105 879088 : switch (node->opcode()) {
106 : case IrOpcode::kHeapConstant: {
107 124566 : HeapObjectRef object(broker, HeapConstantOf(node->op()));
108 249132 : if (object.IsContext()) return object.AsContext();
109 0 : break;
110 : }
111 : case IrOpcode::kParameter: {
112 : OuterContext outer;
113 314859 : if (maybe_outer.To(&outer) && IsContextParameter(node) &&
114 149355 : *distance >= outer.distance) {
115 149355 : *distance -= outer.distance;
116 149355 : return ContextRef(broker, outer.context);
117 : }
118 : break;
119 : }
120 : default:
121 : break;
122 : }
123 605167 : return base::Optional<ContextRef>();
124 : }
125 :
126 : } // anonymous namespace
127 :
128 427181 : Reduction JSContextSpecialization::ReduceJSLoadContext(Node* node) {
129 : DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
130 :
131 427181 : const ContextAccess& access = ContextAccessOf(node->op());
132 427181 : size_t depth = access.depth();
133 :
134 : // First walk up the context chain in the graph as far as possible.
135 427181 : Node* context = NodeProperties::GetOuterContext(node, &depth);
136 :
137 : base::Optional<ContextRef> maybe_concrete =
138 427181 : GetSpecializationContext(broker(), context, &depth, outer());
139 427181 : 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 157097 : return SimplifyJSLoadContext(node, context, depth);
143 : }
144 :
145 : // Now walk up the concrete context chain for the remaining depth.
146 270084 : ContextRef concrete = maybe_concrete.value();
147 270084 : concrete.SerializeContextChain(); // TODO(neis): Remove later.
148 316474 : for (; depth > 0; --depth) {
149 23195 : concrete = concrete.previous();
150 : }
151 :
152 270084 : 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 242359 : return SimplifyJSLoadContext(node, jsgraph()->Constant(concrete), depth);
156 : }
157 :
158 : // This will hold the final value, if we can figure it out.
159 27725 : base::Optional<ObjectRef> maybe_value;
160 :
161 27725 : concrete.SerializeSlot(static_cast<int>(access.index()));
162 55450 : maybe_value = concrete.get(static_cast<int>(access.index()));
163 27725 : 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 25018 : OddballType oddball_type = maybe_value->AsHeapObject().map().oddball_type();
170 50036 : if (oddball_type == OddballType::kUndefined ||
171 25018 : oddball_type == OddballType::kHole) {
172 : maybe_value.reset();
173 : }
174 : }
175 :
176 27725 : if (!maybe_value.has_value()) {
177 547 : 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 27178 : Node* constant = jsgraph_->Constant(*maybe_value);
185 : ReplaceWithValue(node, constant);
186 : return Replace(constant);
187 : }
188 :
189 :
190 451907 : Reduction JSContextSpecialization::ReduceJSStoreContext(Node* node) {
191 : DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());
192 :
193 451907 : const ContextAccess& access = ContextAccessOf(node->op());
194 451907 : 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 451907 : Node* context = NodeProperties::GetOuterContext(node, &depth);
199 :
200 : base::Optional<ContextRef> maybe_concrete =
201 451907 : GetSpecializationContext(broker(), context, &depth, outer());
202 451907 : 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 448070 : return SimplifyJSStoreContext(node, context, depth);
206 : }
207 :
208 : // Now walk up the concrete context chain for the remaining depth.
209 3837 : ContextRef concrete = maybe_concrete.value();
210 3837 : concrete.SerializeContextChain(); // TODO(neis): Remove later.
211 4763 : for (; depth > 0; --depth) {
212 463 : concrete = concrete.previous();
213 : }
214 :
215 3837 : 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 122004 : } // namespace v8
|