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