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