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::kInlineToString:
86 return ReduceToString(node);
87 case Runtime::kInlineCall:
88 return ReduceCall(node);
89 case Runtime::kInlineIncBlockCounter:
90 return ReduceIncBlockCounter(node);
91 case Runtime::kInlineGetImportMetaObject:
92 return ReduceGetImportMetaObject(node);
93 default:
94 break;
95 }
96 return NoChange();
97 }
98
ReduceCopyDataProperties(Node * node)99 Reduction JSIntrinsicLowering::ReduceCopyDataProperties(Node* node) {
100 return Change(
101 node, Builtins::CallableFor(isolate(), Builtins::kCopyDataProperties), 0);
102 }
103
ReduceCreateIterResultObject(Node * node)104 Reduction JSIntrinsicLowering::ReduceCreateIterResultObject(Node* node) {
105 Node* const value = NodeProperties::GetValueInput(node, 0);
106 Node* const done = NodeProperties::GetValueInput(node, 1);
107 Node* const context = NodeProperties::GetContextInput(node);
108 Node* const effect = NodeProperties::GetEffectInput(node);
109 return Change(node, javascript()->CreateIterResultObject(), value, done,
110 context, effect);
111 }
112
ReduceDeoptimizeNow(Node * node)113 Reduction JSIntrinsicLowering::ReduceDeoptimizeNow(Node* node) {
114 Node* const frame_state = NodeProperties::GetFrameStateInput(node);
115 Node* const effect = NodeProperties::GetEffectInput(node);
116 Node* const control = NodeProperties::GetControlInput(node);
117
118 // TODO(bmeurer): Move MergeControlToEnd() to the AdvancedReducer.
119 Node* deoptimize = graph()->NewNode(
120 common()->Deoptimize(DeoptimizeKind::kEager,
121 DeoptimizeReason::kDeoptimizeNow, FeedbackSource()),
122 frame_state, effect, control);
123 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
124 Revisit(graph()->end());
125
126 node->TrimInputCount(0);
127 NodeProperties::ChangeOp(node, common()->Dead());
128 return Changed(node);
129 }
130
ReduceCreateJSGeneratorObject(Node * node)131 Reduction JSIntrinsicLowering::ReduceCreateJSGeneratorObject(Node* node) {
132 Node* const closure = NodeProperties::GetValueInput(node, 0);
133 Node* const receiver = NodeProperties::GetValueInput(node, 1);
134 Node* const context = NodeProperties::GetContextInput(node);
135 Node* const effect = NodeProperties::GetEffectInput(node);
136 Node* const control = NodeProperties::GetControlInput(node);
137 Operator const* const op = javascript()->CreateGeneratorObject();
138 Node* create_generator =
139 graph()->NewNode(op, closure, receiver, context, effect, control);
140 ReplaceWithValue(node, create_generator, create_generator);
141 return Changed(create_generator);
142 }
143
ReduceGeneratorClose(Node * node)144 Reduction JSIntrinsicLowering::ReduceGeneratorClose(Node* node) {
145 Node* const generator = NodeProperties::GetValueInput(node, 0);
146 Node* const effect = NodeProperties::GetEffectInput(node);
147 Node* const control = NodeProperties::GetControlInput(node);
148 Node* const closed = jsgraph()->Constant(JSGeneratorObject::kGeneratorClosed);
149 Node* const undefined = jsgraph()->UndefinedConstant();
150 Operator const* const op = simplified()->StoreField(
151 AccessBuilder::ForJSGeneratorObjectContinuation());
152
153 ReplaceWithValue(node, undefined, node);
154 NodeProperties::RemoveType(node);
155 return Change(node, op, generator, closed, effect, control);
156 }
157
ReduceAsyncFunctionAwaitCaught(Node * node)158 Reduction JSIntrinsicLowering::ReduceAsyncFunctionAwaitCaught(Node* node) {
159 return Change(
160 node,
161 Builtins::CallableFor(isolate(), Builtins::kAsyncFunctionAwaitCaught), 0);
162 }
163
ReduceAsyncFunctionAwaitUncaught(Node * node)164 Reduction JSIntrinsicLowering::ReduceAsyncFunctionAwaitUncaught(Node* node) {
165 return Change(
166 node,
167 Builtins::CallableFor(isolate(), Builtins::kAsyncFunctionAwaitUncaught),
168 0);
169 }
170
ReduceAsyncFunctionEnter(Node * node)171 Reduction JSIntrinsicLowering::ReduceAsyncFunctionEnter(Node* node) {
172 NodeProperties::ChangeOp(node, javascript()->AsyncFunctionEnter());
173 return Changed(node);
174 }
175
ReduceAsyncFunctionReject(Node * node)176 Reduction JSIntrinsicLowering::ReduceAsyncFunctionReject(Node* node) {
177 RelaxControls(node);
178 NodeProperties::ChangeOp(node, javascript()->AsyncFunctionReject());
179 return Changed(node);
180 }
181
ReduceAsyncFunctionResolve(Node * node)182 Reduction JSIntrinsicLowering::ReduceAsyncFunctionResolve(Node* node) {
183 RelaxControls(node);
184 NodeProperties::ChangeOp(node, javascript()->AsyncFunctionResolve());
185 return Changed(node);
186 }
187
ReduceAsyncGeneratorAwaitCaught(Node * node)188 Reduction JSIntrinsicLowering::ReduceAsyncGeneratorAwaitCaught(Node* node) {
189 return Change(
190 node,
191 Builtins::CallableFor(isolate(), Builtins::kAsyncGeneratorAwaitCaught),
192 0);
193 }
194
ReduceAsyncGeneratorAwaitUncaught(Node * node)195 Reduction JSIntrinsicLowering::ReduceAsyncGeneratorAwaitUncaught(Node* node) {
196 return Change(
197 node,
198 Builtins::CallableFor(isolate(), Builtins::kAsyncGeneratorAwaitUncaught),
199 0);
200 }
201
ReduceAsyncGeneratorReject(Node * node)202 Reduction JSIntrinsicLowering::ReduceAsyncGeneratorReject(Node* node) {
203 return Change(
204 node, Builtins::CallableFor(isolate(), Builtins::kAsyncGeneratorReject),
205 0);
206 }
207
ReduceAsyncGeneratorResolve(Node * node)208 Reduction JSIntrinsicLowering::ReduceAsyncGeneratorResolve(Node* node) {
209 return Change(
210 node, Builtins::CallableFor(isolate(), Builtins::kAsyncGeneratorResolve),
211 0);
212 }
213
ReduceAsyncGeneratorYield(Node * node)214 Reduction JSIntrinsicLowering::ReduceAsyncGeneratorYield(Node* node) {
215 return Change(
216 node, Builtins::CallableFor(isolate(), Builtins::kAsyncGeneratorYield),
217 0);
218 }
219
ReduceGeneratorGetResumeMode(Node * node)220 Reduction JSIntrinsicLowering::ReduceGeneratorGetResumeMode(Node* node) {
221 Node* const generator = NodeProperties::GetValueInput(node, 0);
222 Node* const effect = NodeProperties::GetEffectInput(node);
223 Node* const control = NodeProperties::GetControlInput(node);
224 Operator const* const op =
225 simplified()->LoadField(AccessBuilder::ForJSGeneratorObjectResumeMode());
226
227 return Change(node, op, generator, effect, control);
228 }
229
ReduceIsInstanceType(Node * node,InstanceType instance_type)230 Reduction JSIntrinsicLowering::ReduceIsInstanceType(
231 Node* node, InstanceType instance_type) {
232 // if (%_IsSmi(value)) {
233 // return false;
234 // } else {
235 // return %_GetInstanceType(%_GetMap(value)) == instance_type;
236 // }
237 Node* value = NodeProperties::GetValueInput(node, 0);
238 Node* effect = NodeProperties::GetEffectInput(node);
239 Node* control = NodeProperties::GetControlInput(node);
240
241 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value);
242 Node* branch = graph()->NewNode(common()->Branch(), check, control);
243
244 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
245 Node* etrue = effect;
246 Node* vtrue = jsgraph()->FalseConstant();
247
248 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
249 Node* efalse = effect;
250 Node* map = efalse =
251 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), value,
252 efalse, if_false);
253 Node* map_instance_type = efalse = graph()->NewNode(
254 simplified()->LoadField(AccessBuilder::ForMapInstanceType()), map, efalse,
255 if_false);
256 Node* vfalse =
257 graph()->NewNode(simplified()->NumberEqual(), map_instance_type,
258 jsgraph()->Constant(instance_type));
259
260 Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
261
262 // Replace all effect uses of {node} with the {ephi}.
263 Node* ephi = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, merge);
264 ReplaceWithValue(node, node, ephi, merge);
265
266 // Turn the {node} into a Phi.
267 return Change(node, common()->Phi(MachineRepresentation::kTagged, 2), vtrue,
268 vfalse, merge);
269 }
270
271
ReduceIsJSReceiver(Node * node)272 Reduction JSIntrinsicLowering::ReduceIsJSReceiver(Node* node) {
273 return Change(node, simplified()->ObjectIsReceiver());
274 }
275
276
ReduceIsSmi(Node * node)277 Reduction JSIntrinsicLowering::ReduceIsSmi(Node* node) {
278 return Change(node, simplified()->ObjectIsSmi());
279 }
280
ReduceTurbofanStaticAssert(Node * node)281 Reduction JSIntrinsicLowering::ReduceTurbofanStaticAssert(Node* node) {
282 if (FLAG_always_opt) {
283 // Ignore static asserts, as we most likely won't have enough information
284 RelaxEffectsAndControls(node);
285 } else {
286 Node* value = NodeProperties::GetValueInput(node, 0);
287 Node* effect = NodeProperties::GetEffectInput(node);
288 Node* assert = graph()->NewNode(
289 common()->StaticAssert("%TurbofanStaticAssert"), value, effect);
290 ReplaceWithValue(node, node, assert, nullptr);
291 }
292 return Changed(jsgraph_->UndefinedConstant());
293 }
294
ReduceIsBeingInterpreted(Node * node)295 Reduction JSIntrinsicLowering::ReduceIsBeingInterpreted(Node* node) {
296 RelaxEffectsAndControls(node);
297 return Changed(jsgraph_->FalseConstant());
298 }
299
Change(Node * node,const Operator * op)300 Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op) {
301 // Replace all effect uses of {node} with the effect dependency.
302 RelaxEffectsAndControls(node);
303 // Remove the inputs corresponding to context, effect and control.
304 NodeProperties::RemoveNonValueInputs(node);
305 // Finally update the operator to the new one.
306 NodeProperties::ChangeOp(node, op);
307 return Changed(node);
308 }
309
310
ReduceToLength(Node * node)311 Reduction JSIntrinsicLowering::ReduceToLength(Node* node) {
312 NodeProperties::ChangeOp(node, javascript()->ToLength());
313 return Changed(node);
314 }
315
316
ReduceToObject(Node * node)317 Reduction JSIntrinsicLowering::ReduceToObject(Node* node) {
318 NodeProperties::ChangeOp(node, javascript()->ToObject());
319 return Changed(node);
320 }
321
322
ReduceToString(Node * node)323 Reduction JSIntrinsicLowering::ReduceToString(Node* node) {
324 // ToString is unnecessary if the input is a string.
325 HeapObjectMatcher m(NodeProperties::GetValueInput(node, 0));
326 if (m.HasResolvedValue() && m.Ref(broker()).IsString()) {
327 ReplaceWithValue(node, m.node());
328 return Replace(m.node());
329 }
330 NodeProperties::ChangeOp(node, javascript()->ToString());
331 return Changed(node);
332 }
333
334
ReduceCall(Node * node)335 Reduction JSIntrinsicLowering::ReduceCall(Node* node) {
336 int const arity =
337 static_cast<int>(CallRuntimeParametersOf(node->op()).arity());
338 static constexpr int kTargetAndReceiver = 2;
339 STATIC_ASSERT(JSCallNode::kFeedbackVectorIsLastInput);
340 Node* feedback = jsgraph()->UndefinedConstant();
341 node->InsertInput(graph()->zone(), arity, feedback);
342 NodeProperties::ChangeOp(
343 node,
344 javascript()->Call(JSCallNode::ArityForArgc(arity - kTargetAndReceiver)));
345 return Changed(node);
346 }
347
ReduceIncBlockCounter(Node * node)348 Reduction JSIntrinsicLowering::ReduceIncBlockCounter(Node* node) {
349 DCHECK(!Linkage::NeedsFrameStateInput(Runtime::kIncBlockCounter));
350 DCHECK(!Linkage::NeedsFrameStateInput(Runtime::kInlineIncBlockCounter));
351 return Change(node,
352 Builtins::CallableFor(isolate(), Builtins::kIncBlockCounter), 0,
353 kDoesNotNeedFrameState);
354 }
355
ReduceGetImportMetaObject(Node * node)356 Reduction JSIntrinsicLowering::ReduceGetImportMetaObject(Node* node) {
357 NodeProperties::ChangeOp(node, javascript()->GetImportMeta());
358 return Changed(node);
359 }
360
Change(Node * node,const Operator * op,Node * a,Node * b)361 Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op, Node* a,
362 Node* b) {
363 RelaxControls(node);
364 node->ReplaceInput(0, a);
365 node->ReplaceInput(1, b);
366 node->TrimInputCount(2);
367 NodeProperties::ChangeOp(node, op);
368 return Changed(node);
369 }
370
371
Change(Node * node,const Operator * op,Node * a,Node * b,Node * c)372 Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op, Node* a,
373 Node* b, Node* c) {
374 RelaxControls(node);
375 node->ReplaceInput(0, a);
376 node->ReplaceInput(1, b);
377 node->ReplaceInput(2, c);
378 node->TrimInputCount(3);
379 NodeProperties::ChangeOp(node, op);
380 return Changed(node);
381 }
382
383
Change(Node * node,const Operator * op,Node * a,Node * b,Node * c,Node * d)384 Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op, Node* a,
385 Node* b, Node* c, Node* d) {
386 RelaxControls(node);
387 node->ReplaceInput(0, a);
388 node->ReplaceInput(1, b);
389 node->ReplaceInput(2, c);
390 node->ReplaceInput(3, d);
391 node->TrimInputCount(4);
392 NodeProperties::ChangeOp(node, op);
393 return Changed(node);
394 }
395
Change(Node * node,Callable const & callable,int stack_parameter_count,enum FrameStateFlag frame_state_flag)396 Reduction JSIntrinsicLowering::Change(Node* node, Callable const& callable,
397 int stack_parameter_count,
398 enum FrameStateFlag frame_state_flag) {
399 CallDescriptor::Flags flags = frame_state_flag == kNeedsFrameState
400 ? CallDescriptor::kNeedsFrameState
401 : CallDescriptor::kNoFlags;
402 auto call_descriptor = Linkage::GetStubCallDescriptor(
403 graph()->zone(), callable.descriptor(), stack_parameter_count, flags,
404 node->op()->properties());
405 node->InsertInput(graph()->zone(), 0,
406 jsgraph()->HeapConstant(callable.code()));
407 NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
408 return Changed(node);
409 }
410
graph() const411 Graph* JSIntrinsicLowering::graph() const { return jsgraph()->graph(); }
412
413
isolate() const414 Isolate* JSIntrinsicLowering::isolate() const { return jsgraph()->isolate(); }
415
416
common() const417 CommonOperatorBuilder* JSIntrinsicLowering::common() const {
418 return jsgraph()->common();
419 }
420
javascript() const421 JSOperatorBuilder* JSIntrinsicLowering::javascript() const {
422 return jsgraph_->javascript();
423 }
424
simplified() const425 SimplifiedOperatorBuilder* JSIntrinsicLowering::simplified() const {
426 return jsgraph()->simplified();
427 }
428
429 } // namespace compiler
430 } // namespace internal
431 } // namespace v8
432