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-intrinsic-lowering.h" |
6 | |
7 | #include <stack> |
8 | |
9 | #include "src/code-factory.h" |
10 | #include "src/compiler/access-builder.h" |
11 | #include "src/compiler/js-graph.h" |
12 | #include "src/compiler/linkage.h" |
13 | #include "src/compiler/node-matchers.h" |
14 | #include "src/compiler/node-properties.h" |
15 | #include "src/compiler/operator-properties.h" |
16 | #include "src/counters.h" |
17 | #include "src/objects-inl.h" |
18 | #include "src/objects/js-generator.h" |
19 | |
20 | namespace v8 { |
21 | namespace internal { |
22 | namespace compiler { |
23 | |
24 | JSIntrinsicLowering::JSIntrinsicLowering(Editor* editor, JSGraph* jsgraph) |
25 | : AdvancedReducer(editor), jsgraph_(jsgraph) {} |
26 | |
27 | Reduction JSIntrinsicLowering::Reduce(Node* node) { |
28 | if (node->opcode() != IrOpcode::kJSCallRuntime) return NoChange(); |
29 | const Runtime::Function* const f = |
30 | Runtime::FunctionForId(CallRuntimeParametersOf(node->op()).id()); |
31 | if (f->intrinsic_type != Runtime::IntrinsicType::INLINE) return NoChange(); |
32 | switch (f->function_id) { |
33 | case Runtime::kInlineCreateIterResultObject: |
34 | return ReduceCreateIterResultObject(node); |
35 | case Runtime::kInlineDeoptimizeNow: |
36 | return ReduceDeoptimizeNow(node); |
37 | case Runtime::kInlineGeneratorClose: |
38 | return ReduceGeneratorClose(node); |
39 | case Runtime::kInlineCreateJSGeneratorObject: |
40 | return ReduceCreateJSGeneratorObject(node); |
41 | case Runtime::kInlineAsyncFunctionAwaitCaught: |
42 | return ReduceAsyncFunctionAwaitCaught(node); |
43 | case Runtime::kInlineAsyncFunctionAwaitUncaught: |
44 | return ReduceAsyncFunctionAwaitUncaught(node); |
45 | case Runtime::kInlineAsyncFunctionEnter: |
46 | return ReduceAsyncFunctionEnter(node); |
47 | case Runtime::kInlineAsyncFunctionReject: |
48 | return ReduceAsyncFunctionReject(node); |
49 | case Runtime::kInlineAsyncFunctionResolve: |
50 | return ReduceAsyncFunctionResolve(node); |
51 | case Runtime::kInlineAsyncGeneratorAwaitCaught: |
52 | return ReduceAsyncGeneratorAwaitCaught(node); |
53 | case Runtime::kInlineAsyncGeneratorAwaitUncaught: |
54 | return ReduceAsyncGeneratorAwaitUncaught(node); |
55 | case Runtime::kInlineAsyncGeneratorReject: |
56 | return ReduceAsyncGeneratorReject(node); |
57 | case Runtime::kInlineAsyncGeneratorResolve: |
58 | return ReduceAsyncGeneratorResolve(node); |
59 | case Runtime::kInlineAsyncGeneratorYield: |
60 | return ReduceAsyncGeneratorYield(node); |
61 | case Runtime::kInlineGeneratorGetResumeMode: |
62 | return ReduceGeneratorGetResumeMode(node); |
63 | case Runtime::kInlineIsArray: |
64 | return ReduceIsInstanceType(node, JS_ARRAY_TYPE); |
65 | case Runtime::kInlineIsTypedArray: |
66 | return ReduceIsInstanceType(node, JS_TYPED_ARRAY_TYPE); |
67 | case Runtime::kInlineIsJSReceiver: |
68 | return ReduceIsJSReceiver(node); |
69 | case Runtime::kInlineIsSmi: |
70 | return ReduceIsSmi(node); |
71 | case Runtime::kInlineToLength: |
72 | return ReduceToLength(node); |
73 | case Runtime::kInlineToObject: |
74 | return ReduceToObject(node); |
75 | case Runtime::kInlineToString: |
76 | return ReduceToString(node); |
77 | case Runtime::kInlineCall: |
78 | return ReduceCall(node); |
79 | default: |
80 | break; |
81 | } |
82 | return NoChange(); |
83 | } |
84 | |
85 | |
86 | Reduction JSIntrinsicLowering::ReduceCreateIterResultObject(Node* node) { |
87 | Node* const value = NodeProperties::GetValueInput(node, 0); |
88 | Node* const done = NodeProperties::GetValueInput(node, 1); |
89 | Node* const context = NodeProperties::GetContextInput(node); |
90 | Node* const effect = NodeProperties::GetEffectInput(node); |
91 | return Change(node, javascript()->CreateIterResultObject(), value, done, |
92 | context, effect); |
93 | } |
94 | |
95 | Reduction JSIntrinsicLowering::ReduceDeoptimizeNow(Node* node) { |
96 | Node* const frame_state = NodeProperties::GetFrameStateInput(node); |
97 | Node* const effect = NodeProperties::GetEffectInput(node); |
98 | Node* const control = NodeProperties::GetControlInput(node); |
99 | |
100 | // TODO(bmeurer): Move MergeControlToEnd() to the AdvancedReducer. |
101 | Node* deoptimize = graph()->NewNode( |
102 | common()->Deoptimize(DeoptimizeKind::kEager, |
103 | DeoptimizeReason::kDeoptimizeNow, VectorSlotPair()), |
104 | frame_state, effect, control); |
105 | NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); |
106 | Revisit(graph()->end()); |
107 | |
108 | node->TrimInputCount(0); |
109 | NodeProperties::ChangeOp(node, common()->Dead()); |
110 | return Changed(node); |
111 | } |
112 | |
113 | Reduction JSIntrinsicLowering::ReduceCreateJSGeneratorObject(Node* node) { |
114 | Node* const closure = NodeProperties::GetValueInput(node, 0); |
115 | Node* const receiver = NodeProperties::GetValueInput(node, 1); |
116 | Node* const context = NodeProperties::GetContextInput(node); |
117 | Node* const effect = NodeProperties::GetEffectInput(node); |
118 | Node* const control = NodeProperties::GetControlInput(node); |
119 | Operator const* const op = javascript()->CreateGeneratorObject(); |
120 | Node* create_generator = |
121 | graph()->NewNode(op, closure, receiver, context, effect, control); |
122 | ReplaceWithValue(node, create_generator, create_generator); |
123 | return Changed(create_generator); |
124 | } |
125 | |
126 | Reduction JSIntrinsicLowering::ReduceGeneratorClose(Node* node) { |
127 | Node* const generator = NodeProperties::GetValueInput(node, 0); |
128 | Node* const effect = NodeProperties::GetEffectInput(node); |
129 | Node* const control = NodeProperties::GetControlInput(node); |
130 | Node* const closed = jsgraph()->Constant(JSGeneratorObject::kGeneratorClosed); |
131 | Node* const undefined = jsgraph()->UndefinedConstant(); |
132 | Operator const* const op = simplified()->StoreField( |
133 | AccessBuilder::ForJSGeneratorObjectContinuation()); |
134 | |
135 | ReplaceWithValue(node, undefined, node); |
136 | NodeProperties::RemoveType(node); |
137 | return Change(node, op, generator, closed, effect, control); |
138 | } |
139 | |
140 | Reduction JSIntrinsicLowering::ReduceAsyncFunctionAwaitCaught(Node* node) { |
141 | return Change( |
142 | node, |
143 | Builtins::CallableFor(isolate(), Builtins::kAsyncFunctionAwaitCaught), 0); |
144 | } |
145 | |
146 | Reduction JSIntrinsicLowering::ReduceAsyncFunctionAwaitUncaught(Node* node) { |
147 | return Change( |
148 | node, |
149 | Builtins::CallableFor(isolate(), Builtins::kAsyncFunctionAwaitUncaught), |
150 | 0); |
151 | } |
152 | |
153 | Reduction JSIntrinsicLowering::ReduceAsyncFunctionEnter(Node* node) { |
154 | NodeProperties::ChangeOp(node, javascript()->AsyncFunctionEnter()); |
155 | return Changed(node); |
156 | } |
157 | |
158 | Reduction JSIntrinsicLowering::ReduceAsyncFunctionReject(Node* node) { |
159 | RelaxControls(node); |
160 | NodeProperties::ChangeOp(node, javascript()->AsyncFunctionReject()); |
161 | return Changed(node); |
162 | } |
163 | |
164 | Reduction JSIntrinsicLowering::ReduceAsyncFunctionResolve(Node* node) { |
165 | RelaxControls(node); |
166 | NodeProperties::ChangeOp(node, javascript()->AsyncFunctionResolve()); |
167 | return Changed(node); |
168 | } |
169 | |
170 | Reduction JSIntrinsicLowering::ReduceAsyncGeneratorAwaitCaught(Node* node) { |
171 | return Change( |
172 | node, |
173 | Builtins::CallableFor(isolate(), Builtins::kAsyncGeneratorAwaitCaught), |
174 | 0); |
175 | } |
176 | |
177 | Reduction JSIntrinsicLowering::ReduceAsyncGeneratorAwaitUncaught(Node* node) { |
178 | return Change( |
179 | node, |
180 | Builtins::CallableFor(isolate(), Builtins::kAsyncGeneratorAwaitUncaught), |
181 | 0); |
182 | } |
183 | |
184 | Reduction JSIntrinsicLowering::ReduceAsyncGeneratorReject(Node* node) { |
185 | return Change( |
186 | node, Builtins::CallableFor(isolate(), Builtins::kAsyncGeneratorReject), |
187 | 0); |
188 | } |
189 | |
190 | Reduction JSIntrinsicLowering::ReduceAsyncGeneratorResolve(Node* node) { |
191 | return Change( |
192 | node, Builtins::CallableFor(isolate(), Builtins::kAsyncGeneratorResolve), |
193 | 0); |
194 | } |
195 | |
196 | Reduction JSIntrinsicLowering::ReduceAsyncGeneratorYield(Node* node) { |
197 | return Change( |
198 | node, Builtins::CallableFor(isolate(), Builtins::kAsyncGeneratorYield), |
199 | 0); |
200 | } |
201 | |
202 | Reduction JSIntrinsicLowering::ReduceGeneratorGetResumeMode(Node* node) { |
203 | Node* const generator = NodeProperties::GetValueInput(node, 0); |
204 | Node* const effect = NodeProperties::GetEffectInput(node); |
205 | Node* const control = NodeProperties::GetControlInput(node); |
206 | Operator const* const op = |
207 | simplified()->LoadField(AccessBuilder::ForJSGeneratorObjectResumeMode()); |
208 | |
209 | return Change(node, op, generator, effect, control); |
210 | } |
211 | |
212 | Reduction JSIntrinsicLowering::ReduceIsInstanceType( |
213 | Node* node, InstanceType instance_type) { |
214 | // if (%_IsSmi(value)) { |
215 | // return false; |
216 | // } else { |
217 | // return %_GetInstanceType(%_GetMap(value)) == instance_type; |
218 | // } |
219 | Node* value = NodeProperties::GetValueInput(node, 0); |
220 | Node* effect = NodeProperties::GetEffectInput(node); |
221 | Node* control = NodeProperties::GetControlInput(node); |
222 | |
223 | Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value); |
224 | Node* branch = graph()->NewNode(common()->Branch(), check, control); |
225 | |
226 | Node* if_true = graph()->NewNode(common()->IfTrue(), branch); |
227 | Node* etrue = effect; |
228 | Node* vtrue = jsgraph()->FalseConstant(); |
229 | |
230 | Node* if_false = graph()->NewNode(common()->IfFalse(), branch); |
231 | Node* efalse = effect; |
232 | Node* map = efalse = |
233 | graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), value, |
234 | efalse, if_false); |
235 | Node* map_instance_type = efalse = graph()->NewNode( |
236 | simplified()->LoadField(AccessBuilder::ForMapInstanceType()), map, efalse, |
237 | if_false); |
238 | Node* vfalse = |
239 | graph()->NewNode(simplified()->NumberEqual(), map_instance_type, |
240 | jsgraph()->Constant(instance_type)); |
241 | |
242 | Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false); |
243 | |
244 | // Replace all effect uses of {node} with the {ephi}. |
245 | Node* ephi = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, merge); |
246 | ReplaceWithValue(node, node, ephi, merge); |
247 | |
248 | // Turn the {node} into a Phi. |
249 | return Change(node, common()->Phi(MachineRepresentation::kTagged, 2), vtrue, |
250 | vfalse, merge); |
251 | } |
252 | |
253 | |
254 | Reduction JSIntrinsicLowering::ReduceIsJSReceiver(Node* node) { |
255 | return Change(node, simplified()->ObjectIsReceiver()); |
256 | } |
257 | |
258 | |
259 | Reduction JSIntrinsicLowering::ReduceIsSmi(Node* node) { |
260 | return Change(node, simplified()->ObjectIsSmi()); |
261 | } |
262 | |
263 | Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op) { |
264 | // Replace all effect uses of {node} with the effect dependency. |
265 | RelaxEffectsAndControls(node); |
266 | // Remove the inputs corresponding to context, effect and control. |
267 | NodeProperties::RemoveNonValueInputs(node); |
268 | // Finally update the operator to the new one. |
269 | NodeProperties::ChangeOp(node, op); |
270 | return Changed(node); |
271 | } |
272 | |
273 | |
274 | Reduction JSIntrinsicLowering::ReduceToLength(Node* node) { |
275 | NodeProperties::ChangeOp(node, javascript()->ToLength()); |
276 | return Changed(node); |
277 | } |
278 | |
279 | |
280 | Reduction JSIntrinsicLowering::ReduceToObject(Node* node) { |
281 | NodeProperties::ChangeOp(node, javascript()->ToObject()); |
282 | return Changed(node); |
283 | } |
284 | |
285 | |
286 | Reduction JSIntrinsicLowering::ReduceToString(Node* node) { |
287 | // ToString is unnecessary if the input is a string. |
288 | HeapObjectMatcher m(NodeProperties::GetValueInput(node, 0)); |
289 | if (m.HasValue() && m.Value()->IsString()) { |
290 | ReplaceWithValue(node, m.node()); |
291 | return Replace(m.node()); |
292 | } |
293 | NodeProperties::ChangeOp(node, javascript()->ToString()); |
294 | return Changed(node); |
295 | } |
296 | |
297 | |
298 | Reduction JSIntrinsicLowering::ReduceCall(Node* node) { |
299 | size_t const arity = CallRuntimeParametersOf(node->op()).arity(); |
300 | NodeProperties::ChangeOp(node, javascript()->Call(arity)); |
301 | return Changed(node); |
302 | } |
303 | |
304 | Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op, Node* a, |
305 | Node* b) { |
306 | RelaxControls(node); |
307 | node->ReplaceInput(0, a); |
308 | node->ReplaceInput(1, b); |
309 | node->TrimInputCount(2); |
310 | NodeProperties::ChangeOp(node, op); |
311 | return Changed(node); |
312 | } |
313 | |
314 | |
315 | Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op, Node* a, |
316 | Node* b, Node* c) { |
317 | RelaxControls(node); |
318 | node->ReplaceInput(0, a); |
319 | node->ReplaceInput(1, b); |
320 | node->ReplaceInput(2, c); |
321 | node->TrimInputCount(3); |
322 | NodeProperties::ChangeOp(node, op); |
323 | return Changed(node); |
324 | } |
325 | |
326 | |
327 | Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op, Node* a, |
328 | Node* b, Node* c, Node* d) { |
329 | RelaxControls(node); |
330 | node->ReplaceInput(0, a); |
331 | node->ReplaceInput(1, b); |
332 | node->ReplaceInput(2, c); |
333 | node->ReplaceInput(3, d); |
334 | node->TrimInputCount(4); |
335 | NodeProperties::ChangeOp(node, op); |
336 | return Changed(node); |
337 | } |
338 | |
339 | |
340 | Reduction JSIntrinsicLowering::Change(Node* node, Callable const& callable, |
341 | int stack_parameter_count) { |
342 | auto call_descriptor = Linkage::GetStubCallDescriptor( |
343 | graph()->zone(), callable.descriptor(), stack_parameter_count, |
344 | CallDescriptor::kNeedsFrameState, node->op()->properties()); |
345 | node->InsertInput(graph()->zone(), 0, |
346 | jsgraph()->HeapConstant(callable.code())); |
347 | NodeProperties::ChangeOp(node, common()->Call(call_descriptor)); |
348 | return Changed(node); |
349 | } |
350 | |
351 | |
352 | Graph* JSIntrinsicLowering::graph() const { return jsgraph()->graph(); } |
353 | |
354 | |
355 | Isolate* JSIntrinsicLowering::isolate() const { return jsgraph()->isolate(); } |
356 | |
357 | |
358 | CommonOperatorBuilder* JSIntrinsicLowering::common() const { |
359 | return jsgraph()->common(); |
360 | } |
361 | |
362 | JSOperatorBuilder* JSIntrinsicLowering::javascript() const { |
363 | return jsgraph_->javascript(); |
364 | } |
365 | |
366 | SimplifiedOperatorBuilder* JSIntrinsicLowering::simplified() const { |
367 | return jsgraph()->simplified(); |
368 | } |
369 | |
370 | } // namespace compiler |
371 | } // namespace internal |
372 | } // namespace v8 |
373 | |