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 if (node->opcode() != IrOpcode::kJSCallRuntime) return NoChange();
31 const Runtime::Function* const f =
32 Runtime::FunctionForId(CallRuntimeParametersOf(node->op()).id());
33 switch (f->function_id) {
34 case Runtime::kIsBeingInterpreted:
35 return ReduceIsBeingInterpreted(node);
36 case Runtime::kTurbofanStaticAssert:
37 return ReduceTurbofanStaticAssert(node);
38 case Runtime::kVerifyType:
39 return ReduceVerifyType(node);
40 default:
41 break;
42 }
43 if (f->intrinsic_type != Runtime::IntrinsicType::INLINE) return NoChange();
44 switch (f->function_id) {
45 case Runtime::kInlineCopyDataProperties:
46 return ReduceCopyDataProperties(node);
47 case Runtime::kInlineCopyDataPropertiesWithExcludedPropertiesOnStack:
48 return ReduceCopyDataPropertiesWithExcludedPropertiesOnStack(node);
49 case Runtime::kInlineCreateIterResultObject:
50 return ReduceCreateIterResultObject(node);
51 case Runtime::kInlineDeoptimizeNow:
52 return ReduceDeoptimizeNow(node);
53 case Runtime::kInlineGeneratorClose:
54 return ReduceGeneratorClose(node);
55 case Runtime::kInlineCreateJSGeneratorObject:
56 return ReduceCreateJSGeneratorObject(node);
57 case Runtime::kInlineAsyncFunctionAwaitCaught:
58 return ReduceAsyncFunctionAwaitCaught(node);
59 case Runtime::kInlineAsyncFunctionAwaitUncaught:
60 return ReduceAsyncFunctionAwaitUncaught(node);
61 case Runtime::kInlineAsyncFunctionEnter:
62 return ReduceAsyncFunctionEnter(node);
63 case Runtime::kInlineAsyncFunctionReject:
64 return ReduceAsyncFunctionReject(node);
65 case Runtime::kInlineAsyncFunctionResolve:
66 return ReduceAsyncFunctionResolve(node);
67 case Runtime::kInlineAsyncGeneratorAwaitCaught:
68 return ReduceAsyncGeneratorAwaitCaught(node);
69 case Runtime::kInlineAsyncGeneratorAwaitUncaught:
70 return ReduceAsyncGeneratorAwaitUncaught(node);
71 case Runtime::kInlineAsyncGeneratorReject:
72 return ReduceAsyncGeneratorReject(node);
73 case Runtime::kInlineAsyncGeneratorResolve:
74 return ReduceAsyncGeneratorResolve(node);
75 case Runtime::kInlineAsyncGeneratorYield:
76 return ReduceAsyncGeneratorYield(node);
77 case Runtime::kInlineGeneratorGetResumeMode:
78 return ReduceGeneratorGetResumeMode(node);
79 case Runtime::kInlineIncBlockCounter:
80 return ReduceIncBlockCounter(node);
81 case Runtime::kInlineGetImportMetaObject:
82 return ReduceGetImportMetaObject(node);
83 default:
84 break;
85 }
86 return NoChange();
87 }
88
ReduceCopyDataProperties(Node * node)89 Reduction JSIntrinsicLowering::ReduceCopyDataProperties(Node* node) {
90 return Change(
91 node, Builtins::CallableFor(isolate(), Builtin::kCopyDataProperties), 0);
92 }
93
94 Reduction
ReduceCopyDataPropertiesWithExcludedPropertiesOnStack(Node * node)95 JSIntrinsicLowering::ReduceCopyDataPropertiesWithExcludedPropertiesOnStack(
96 Node* node) {
97 int input_count =
98 static_cast<int>(CallRuntimeParametersOf(node->op()).arity());
99 CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
100 auto callable = Builtins::CallableFor(
101 isolate(), Builtin::kCopyDataPropertiesWithExcludedProperties);
102 auto call_descriptor = Linkage::GetStubCallDescriptor(
103 graph()->zone(), callable.descriptor(), input_count - 1, flags,
104 node->op()->properties());
105 node->InsertInput(graph()->zone(), 0,
106 jsgraph()->HeapConstant(callable.code()));
107 node->InsertInput(graph()->zone(), 2,
108 jsgraph()->SmiConstant(input_count - 1));
109 NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
110 return Changed(node);
111 }
112
ReduceCreateIterResultObject(Node * node)113 Reduction JSIntrinsicLowering::ReduceCreateIterResultObject(Node* node) {
114 Node* const value = NodeProperties::GetValueInput(node, 0);
115 Node* const done = NodeProperties::GetValueInput(node, 1);
116 Node* const context = NodeProperties::GetContextInput(node);
117 Node* const effect = NodeProperties::GetEffectInput(node);
118 return Change(node, javascript()->CreateIterResultObject(), value, done,
119 context, effect);
120 }
121
ReduceDeoptimizeNow(Node * node)122 Reduction JSIntrinsicLowering::ReduceDeoptimizeNow(Node* node) {
123 Node* const frame_state = NodeProperties::GetFrameStateInput(node);
124 Node* const effect = NodeProperties::GetEffectInput(node);
125 Node* const control = NodeProperties::GetControlInput(node);
126
127 // TODO(bmeurer): Move MergeControlToEnd() to the AdvancedReducer.
128 Node* deoptimize = graph()->NewNode(
129 common()->Deoptimize(DeoptimizeReason::kDeoptimizeNow, FeedbackSource()),
130 frame_state, effect, control);
131 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
132 Revisit(graph()->end());
133
134 node->TrimInputCount(0);
135 NodeProperties::ChangeOp(node, common()->Dead());
136 return Changed(node);
137 }
138
ReduceCreateJSGeneratorObject(Node * node)139 Reduction JSIntrinsicLowering::ReduceCreateJSGeneratorObject(Node* node) {
140 Node* const closure = NodeProperties::GetValueInput(node, 0);
141 Node* const receiver = NodeProperties::GetValueInput(node, 1);
142 Node* const context = NodeProperties::GetContextInput(node);
143 Node* const effect = NodeProperties::GetEffectInput(node);
144 Node* const control = NodeProperties::GetControlInput(node);
145 Operator const* const op = javascript()->CreateGeneratorObject();
146 Node* create_generator =
147 graph()->NewNode(op, closure, receiver, context, effect, control);
148 ReplaceWithValue(node, create_generator, create_generator);
149 return Changed(create_generator);
150 }
151
ReduceGeneratorClose(Node * node)152 Reduction JSIntrinsicLowering::ReduceGeneratorClose(Node* node) {
153 Node* const generator = NodeProperties::GetValueInput(node, 0);
154 Node* const effect = NodeProperties::GetEffectInput(node);
155 Node* const control = NodeProperties::GetControlInput(node);
156 Node* const closed = jsgraph()->Constant(JSGeneratorObject::kGeneratorClosed);
157 Node* const undefined = jsgraph()->UndefinedConstant();
158 Operator const* const op = simplified()->StoreField(
159 AccessBuilder::ForJSGeneratorObjectContinuation());
160
161 ReplaceWithValue(node, undefined, node);
162 NodeProperties::RemoveType(node);
163 return Change(node, op, generator, closed, effect, control);
164 }
165
ReduceAsyncFunctionAwaitCaught(Node * node)166 Reduction JSIntrinsicLowering::ReduceAsyncFunctionAwaitCaught(Node* node) {
167 return Change(
168 node,
169 Builtins::CallableFor(isolate(), Builtin::kAsyncFunctionAwaitCaught), 0);
170 }
171
ReduceAsyncFunctionAwaitUncaught(Node * node)172 Reduction JSIntrinsicLowering::ReduceAsyncFunctionAwaitUncaught(Node* node) {
173 return Change(
174 node,
175 Builtins::CallableFor(isolate(), Builtin::kAsyncFunctionAwaitUncaught),
176 0);
177 }
178
ReduceAsyncFunctionEnter(Node * node)179 Reduction JSIntrinsicLowering::ReduceAsyncFunctionEnter(Node* node) {
180 NodeProperties::ChangeOp(node, javascript()->AsyncFunctionEnter());
181 return Changed(node);
182 }
183
ReduceAsyncFunctionReject(Node * node)184 Reduction JSIntrinsicLowering::ReduceAsyncFunctionReject(Node* node) {
185 RelaxControls(node);
186 NodeProperties::ChangeOp(node, javascript()->AsyncFunctionReject());
187 return Changed(node);
188 }
189
ReduceAsyncFunctionResolve(Node * node)190 Reduction JSIntrinsicLowering::ReduceAsyncFunctionResolve(Node* node) {
191 RelaxControls(node);
192 NodeProperties::ChangeOp(node, javascript()->AsyncFunctionResolve());
193 return Changed(node);
194 }
195
ReduceAsyncGeneratorAwaitCaught(Node * node)196 Reduction JSIntrinsicLowering::ReduceAsyncGeneratorAwaitCaught(Node* node) {
197 return Change(
198 node,
199 Builtins::CallableFor(isolate(), Builtin::kAsyncGeneratorAwaitCaught), 0);
200 }
201
ReduceAsyncGeneratorAwaitUncaught(Node * node)202 Reduction JSIntrinsicLowering::ReduceAsyncGeneratorAwaitUncaught(Node* node) {
203 return Change(
204 node,
205 Builtins::CallableFor(isolate(), Builtin::kAsyncGeneratorAwaitUncaught),
206 0);
207 }
208
ReduceAsyncGeneratorReject(Node * node)209 Reduction JSIntrinsicLowering::ReduceAsyncGeneratorReject(Node* node) {
210 return Change(
211 node, Builtins::CallableFor(isolate(), Builtin::kAsyncGeneratorReject),
212 0);
213 }
214
ReduceAsyncGeneratorResolve(Node * node)215 Reduction JSIntrinsicLowering::ReduceAsyncGeneratorResolve(Node* node) {
216 return Change(
217 node, Builtins::CallableFor(isolate(), Builtin::kAsyncGeneratorResolve),
218 0);
219 }
220
ReduceAsyncGeneratorYield(Node * node)221 Reduction JSIntrinsicLowering::ReduceAsyncGeneratorYield(Node* node) {
222 return Change(
223 node, Builtins::CallableFor(isolate(), Builtin::kAsyncGeneratorYield), 0);
224 }
225
ReduceGeneratorGetResumeMode(Node * node)226 Reduction JSIntrinsicLowering::ReduceGeneratorGetResumeMode(Node* node) {
227 Node* const generator = NodeProperties::GetValueInput(node, 0);
228 Node* const effect = NodeProperties::GetEffectInput(node);
229 Node* const control = NodeProperties::GetControlInput(node);
230 Operator const* const op =
231 simplified()->LoadField(AccessBuilder::ForJSGeneratorObjectResumeMode());
232
233 return Change(node, op, generator, effect, control);
234 }
235
ReduceIsInstanceType(Node * node,InstanceType instance_type)236 Reduction JSIntrinsicLowering::ReduceIsInstanceType(
237 Node* node, InstanceType instance_type) {
238 // if (%_IsSmi(value)) {
239 // return false;
240 // } else {
241 // return %_GetInstanceType(%_GetMap(value)) == instance_type;
242 // }
243 Node* value = NodeProperties::GetValueInput(node, 0);
244 Node* effect = NodeProperties::GetEffectInput(node);
245 Node* control = NodeProperties::GetControlInput(node);
246
247 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value);
248 Node* branch = graph()->NewNode(common()->Branch(), check, control);
249
250 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
251 Node* etrue = effect;
252 Node* vtrue = jsgraph()->FalseConstant();
253
254 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
255 Node* efalse = effect;
256 Node* map = efalse =
257 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), value,
258 efalse, if_false);
259 Node* map_instance_type = efalse = graph()->NewNode(
260 simplified()->LoadField(AccessBuilder::ForMapInstanceType()), map, efalse,
261 if_false);
262 Node* vfalse =
263 graph()->NewNode(simplified()->NumberEqual(), map_instance_type,
264 jsgraph()->Constant(instance_type));
265
266 Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
267
268 // Replace all effect uses of {node} with the {ephi}.
269 Node* ephi = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, merge);
270 ReplaceWithValue(node, node, ephi, merge);
271
272 // Turn the {node} into a Phi.
273 return Change(node, common()->Phi(MachineRepresentation::kTagged, 2), vtrue,
274 vfalse, merge);
275 }
276
ReduceIsJSReceiver(Node * node)277 Reduction JSIntrinsicLowering::ReduceIsJSReceiver(Node* node) {
278 return Change(node, simplified()->ObjectIsReceiver());
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
ReduceVerifyType(Node * node)295 Reduction JSIntrinsicLowering::ReduceVerifyType(Node* node) {
296 return Change(node, simplified()->VerifyType());
297 }
298
ReduceIsBeingInterpreted(Node * node)299 Reduction JSIntrinsicLowering::ReduceIsBeingInterpreted(Node* node) {
300 RelaxEffectsAndControls(node);
301 return Changed(jsgraph_->FalseConstant());
302 }
303
Change(Node * node,const Operator * op)304 Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op) {
305 // Replace all effect uses of {node} with the effect dependency.
306 RelaxEffectsAndControls(node);
307 // Remove the inputs corresponding to context, effect and control.
308 NodeProperties::RemoveNonValueInputs(node);
309 // Finally update the operator to the new one.
310 NodeProperties::ChangeOp(node, op);
311 return Changed(node);
312 }
313
ReduceToLength(Node * node)314 Reduction JSIntrinsicLowering::ReduceToLength(Node* node) {
315 NodeProperties::ChangeOp(node, javascript()->ToLength());
316 return Changed(node);
317 }
318
ReduceToObject(Node * node)319 Reduction JSIntrinsicLowering::ReduceToObject(Node* node) {
320 NodeProperties::ChangeOp(node, javascript()->ToObject());
321 return Changed(node);
322 }
323
ReduceToString(Node * node)324 Reduction JSIntrinsicLowering::ReduceToString(Node* node) {
325 // ToString is unnecessary if the input is a string.
326 HeapObjectMatcher m(NodeProperties::GetValueInput(node, 0));
327 if (m.HasResolvedValue() && m.Ref(broker()).IsString()) {
328 ReplaceWithValue(node, m.node());
329 return Replace(m.node());
330 }
331 NodeProperties::ChangeOp(node, javascript()->ToString());
332 return Changed(node);
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(), Builtin::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
Change(Node * node,const Operator * op,Node * a,Node * b,Node * c)371 Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op, Node* a,
372 Node* b, Node* c) {
373 RelaxControls(node);
374 node->ReplaceInput(0, a);
375 node->ReplaceInput(1, b);
376 node->ReplaceInput(2, c);
377 node->TrimInputCount(3);
378 NodeProperties::ChangeOp(node, op);
379 return Changed(node);
380 }
381
Change(Node * node,const Operator * op,Node * a,Node * b,Node * c,Node * d)382 Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op, Node* a,
383 Node* b, Node* c, Node* d) {
384 RelaxControls(node);
385 node->ReplaceInput(0, a);
386 node->ReplaceInput(1, b);
387 node->ReplaceInput(2, c);
388 node->ReplaceInput(3, d);
389 node->TrimInputCount(4);
390 NodeProperties::ChangeOp(node, op);
391 return Changed(node);
392 }
393
Change(Node * node,Callable const & callable,int stack_parameter_count,enum FrameStateFlag frame_state_flag)394 Reduction JSIntrinsicLowering::Change(Node* node, Callable const& callable,
395 int stack_parameter_count,
396 enum FrameStateFlag frame_state_flag) {
397 CallDescriptor::Flags flags = frame_state_flag == kNeedsFrameState
398 ? CallDescriptor::kNeedsFrameState
399 : CallDescriptor::kNoFlags;
400 auto call_descriptor = Linkage::GetStubCallDescriptor(
401 graph()->zone(), callable.descriptor(), stack_parameter_count, flags,
402 node->op()->properties());
403 node->InsertInput(graph()->zone(), 0,
404 jsgraph()->HeapConstant(callable.code()));
405 NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
406 return Changed(node);
407 }
408
graph() const409 Graph* JSIntrinsicLowering::graph() const { return jsgraph()->graph(); }
410
isolate() const411 Isolate* JSIntrinsicLowering::isolate() const { return jsgraph()->isolate(); }
412
common() const413 CommonOperatorBuilder* JSIntrinsicLowering::common() const {
414 return jsgraph()->common();
415 }
416
javascript() const417 JSOperatorBuilder* JSIntrinsicLowering::javascript() const {
418 return jsgraph_->javascript();
419 }
420
simplified() const421 SimplifiedOperatorBuilder* JSIntrinsicLowering::simplified() const {
422 return jsgraph()->simplified();
423 }
424
425 } // namespace compiler
426 } // namespace internal
427 } // namespace v8
428