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
21namespace v8 {
22namespace internal {
23namespace compiler {
24
25JSIntrinsicLowering::JSIntrinsicLowering(Editor* editor, JSGraph* jsgraph,
26                                         JSHeapBroker* broker)
27    : AdvancedReducer(editor), jsgraph_(jsgraph), broker_(broker) {}
28
29Reduction 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
89Reduction JSIntrinsicLowering::ReduceCopyDataProperties(Node* node) {
90  return Change(
91      node, Builtins::CallableFor(isolate(), Builtin::kCopyDataProperties), 0);
92}
93
94Reduction
95JSIntrinsicLowering::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
113Reduction 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
122Reduction 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
139Reduction 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
152Reduction 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
166Reduction JSIntrinsicLowering::ReduceAsyncFunctionAwaitCaught(Node* node) {
167  return Change(
168      node,
169      Builtins::CallableFor(isolate(), Builtin::kAsyncFunctionAwaitCaught), 0);
170}
171
172Reduction JSIntrinsicLowering::ReduceAsyncFunctionAwaitUncaught(Node* node) {
173  return Change(
174      node,
175      Builtins::CallableFor(isolate(), Builtin::kAsyncFunctionAwaitUncaught),
176      0);
177}
178
179Reduction JSIntrinsicLowering::ReduceAsyncFunctionEnter(Node* node) {
180  NodeProperties::ChangeOp(node, javascript()->AsyncFunctionEnter());
181  return Changed(node);
182}
183
184Reduction JSIntrinsicLowering::ReduceAsyncFunctionReject(Node* node) {
185  RelaxControls(node);
186  NodeProperties::ChangeOp(node, javascript()->AsyncFunctionReject());
187  return Changed(node);
188}
189
190Reduction JSIntrinsicLowering::ReduceAsyncFunctionResolve(Node* node) {
191  RelaxControls(node);
192  NodeProperties::ChangeOp(node, javascript()->AsyncFunctionResolve());
193  return Changed(node);
194}
195
196Reduction JSIntrinsicLowering::ReduceAsyncGeneratorAwaitCaught(Node* node) {
197  return Change(
198      node,
199      Builtins::CallableFor(isolate(), Builtin::kAsyncGeneratorAwaitCaught), 0);
200}
201
202Reduction JSIntrinsicLowering::ReduceAsyncGeneratorAwaitUncaught(Node* node) {
203  return Change(
204      node,
205      Builtins::CallableFor(isolate(), Builtin::kAsyncGeneratorAwaitUncaught),
206      0);
207}
208
209Reduction JSIntrinsicLowering::ReduceAsyncGeneratorReject(Node* node) {
210  return Change(
211      node, Builtins::CallableFor(isolate(), Builtin::kAsyncGeneratorReject),
212      0);
213}
214
215Reduction JSIntrinsicLowering::ReduceAsyncGeneratorResolve(Node* node) {
216  return Change(
217      node, Builtins::CallableFor(isolate(), Builtin::kAsyncGeneratorResolve),
218      0);
219}
220
221Reduction JSIntrinsicLowering::ReduceAsyncGeneratorYield(Node* node) {
222  return Change(
223      node, Builtins::CallableFor(isolate(), Builtin::kAsyncGeneratorYield), 0);
224}
225
226Reduction 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
236Reduction 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
277Reduction JSIntrinsicLowering::ReduceIsJSReceiver(Node* node) {
278  return Change(node, simplified()->ObjectIsReceiver());
279}
280
281Reduction 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
295Reduction JSIntrinsicLowering::ReduceVerifyType(Node* node) {
296  return Change(node, simplified()->VerifyType());
297}
298
299Reduction JSIntrinsicLowering::ReduceIsBeingInterpreted(Node* node) {
300  RelaxEffectsAndControls(node);
301  return Changed(jsgraph_->FalseConstant());
302}
303
304Reduction 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
314Reduction JSIntrinsicLowering::ReduceToLength(Node* node) {
315  NodeProperties::ChangeOp(node, javascript()->ToLength());
316  return Changed(node);
317}
318
319Reduction JSIntrinsicLowering::ReduceToObject(Node* node) {
320  NodeProperties::ChangeOp(node, javascript()->ToObject());
321  return Changed(node);
322}
323
324Reduction 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
335Reduction 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
348Reduction 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
356Reduction JSIntrinsicLowering::ReduceGetImportMetaObject(Node* node) {
357  NodeProperties::ChangeOp(node, javascript()->GetImportMeta());
358  return Changed(node);
359}
360
361Reduction 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
371Reduction 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
382Reduction 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
394Reduction 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
409Graph* JSIntrinsicLowering::graph() const { return jsgraph()->graph(); }
410
411Isolate* JSIntrinsicLowering::isolate() const { return jsgraph()->isolate(); }
412
413CommonOperatorBuilder* JSIntrinsicLowering::common() const {
414  return jsgraph()->common();
415}
416
417JSOperatorBuilder* JSIntrinsicLowering::javascript() const {
418  return jsgraph_->javascript();
419}
420
421SimplifiedOperatorBuilder* JSIntrinsicLowering::simplified() const {
422  return jsgraph()->simplified();
423}
424
425}  // namespace compiler
426}  // namespace internal
427}  // namespace v8
428