1 // Copyright 2014 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-generic-lowering.h"
6
7 #include "src/ast/ast.h"
8 #include "src/builtins/builtins-constructor.h"
9 #include "src/codegen/code-factory.h"
10 #include "src/codegen/interface-descriptors-inl.h"
11 #include "src/compiler/access-builder.h"
12 #include "src/compiler/common-operator.h"
13 #include "src/compiler/js-graph.h"
14 #include "src/compiler/js-heap-broker.h"
15 #include "src/compiler/machine-operator.h"
16 #include "src/compiler/node-matchers.h"
17 #include "src/compiler/node-properties.h"
18 #include "src/compiler/operator-properties.h"
19 #include "src/compiler/processed-feedback.h"
20 #include "src/compiler/simplified-operator.h"
21 #include "src/objects/feedback-cell.h"
22 #include "src/objects/feedback-vector.h"
23 #include "src/objects/scope-info.h"
24 #include "src/objects/template-objects-inl.h"
25
26 namespace v8 {
27 namespace internal {
28 namespace compiler {
29
30 namespace {
31
FrameStateFlagForCall(Node * node)32 CallDescriptor::Flags FrameStateFlagForCall(Node* node) {
33 return OperatorProperties::HasFrameStateInput(node->op())
34 ? CallDescriptor::kNeedsFrameState
35 : CallDescriptor::kNoFlags;
36 }
37
38 } // namespace
39
JSGenericLowering(JSGraph * jsgraph,Editor * editor,JSHeapBroker * broker)40 JSGenericLowering::JSGenericLowering(JSGraph* jsgraph, Editor* editor,
41 JSHeapBroker* broker)
42 : AdvancedReducer(editor), jsgraph_(jsgraph), broker_(broker) {}
43
44 JSGenericLowering::~JSGenericLowering() = default;
45
46
Reduce(Node * node)47 Reduction JSGenericLowering::Reduce(Node* node) {
48 switch (node->opcode()) {
49 #define DECLARE_CASE(x, ...) \
50 case IrOpcode::k##x: \
51 Lower##x(node); \
52 break;
53 JS_OP_LIST(DECLARE_CASE)
54 #undef DECLARE_CASE
55 default:
56 // Nothing to see.
57 return NoChange();
58 }
59 return Changed(node);
60 }
61
62 #define REPLACE_STUB_CALL(Name) \
63 void JSGenericLowering::LowerJS##Name(Node* node) { \
64 ReplaceWithBuiltinCall(node, Builtin::k##Name); \
65 }
66 REPLACE_STUB_CALL(ToLength)
REPLACE_STUB_CALL(ToNumber)67 REPLACE_STUB_CALL(ToNumber)
68 REPLACE_STUB_CALL(ToNumberConvertBigInt)
69 REPLACE_STUB_CALL(ToNumeric)
70 REPLACE_STUB_CALL(ToName)
71 REPLACE_STUB_CALL(ToObject)
72 REPLACE_STUB_CALL(ToString)
73 REPLACE_STUB_CALL(ForInEnumerate)
74 REPLACE_STUB_CALL(AsyncFunctionEnter)
75 REPLACE_STUB_CALL(AsyncFunctionReject)
76 REPLACE_STUB_CALL(AsyncFunctionResolve)
77 REPLACE_STUB_CALL(FulfillPromise)
78 REPLACE_STUB_CALL(PerformPromiseThen)
79 REPLACE_STUB_CALL(PromiseResolve)
80 REPLACE_STUB_CALL(RejectPromise)
81 REPLACE_STUB_CALL(ResolvePromise)
82 #undef REPLACE_STUB_CALL
83
84 void JSGenericLowering::ReplaceWithBuiltinCall(Node* node, Builtin builtin) {
85 CallDescriptor::Flags flags = FrameStateFlagForCall(node);
86 Callable callable = Builtins::CallableFor(isolate(), builtin);
87 ReplaceWithBuiltinCall(node, callable, flags);
88 }
89
ReplaceWithBuiltinCall(Node * node,Callable callable,CallDescriptor::Flags flags)90 void JSGenericLowering::ReplaceWithBuiltinCall(Node* node, Callable callable,
91 CallDescriptor::Flags flags) {
92 ReplaceWithBuiltinCall(node, callable, flags, node->op()->properties());
93 }
94
ReplaceWithBuiltinCall(Node * node,Callable callable,CallDescriptor::Flags flags,Operator::Properties properties)95 void JSGenericLowering::ReplaceWithBuiltinCall(
96 Node* node, Callable callable, CallDescriptor::Flags flags,
97 Operator::Properties properties) {
98 const CallInterfaceDescriptor& descriptor = callable.descriptor();
99 auto call_descriptor = Linkage::GetStubCallDescriptor(
100 zone(), descriptor, descriptor.GetStackParameterCount(), flags,
101 properties);
102 Node* stub_code = jsgraph()->HeapConstant(callable.code());
103 node->InsertInput(zone(), 0, stub_code);
104 NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
105 }
106
ReplaceWithRuntimeCall(Node * node,Runtime::FunctionId f,int nargs_override)107 void JSGenericLowering::ReplaceWithRuntimeCall(Node* node,
108 Runtime::FunctionId f,
109 int nargs_override) {
110 CallDescriptor::Flags flags = FrameStateFlagForCall(node);
111 Operator::Properties properties = node->op()->properties();
112 const Runtime::Function* fun = Runtime::FunctionForId(f);
113 int nargs = (nargs_override < 0) ? fun->nargs : nargs_override;
114 auto call_descriptor =
115 Linkage::GetRuntimeCallDescriptor(zone(), f, nargs, properties, flags);
116 Node* ref = jsgraph()->ExternalConstant(ExternalReference::Create(f));
117 Node* arity = jsgraph()->Int32Constant(nargs);
118 node->InsertInput(zone(), 0, jsgraph()->CEntryStubConstant(fun->result_size));
119 node->InsertInput(zone(), nargs + 1, ref);
120 node->InsertInput(zone(), nargs + 2, arity);
121 NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
122 }
123
ReplaceUnaryOpWithBuiltinCall(Node * node,Builtin builtin_without_feedback,Builtin builtin_with_feedback)124 void JSGenericLowering::ReplaceUnaryOpWithBuiltinCall(
125 Node* node, Builtin builtin_without_feedback,
126 Builtin builtin_with_feedback) {
127 DCHECK(JSOperator::IsUnaryWithFeedback(node->opcode()));
128 const FeedbackParameter& p = FeedbackParameterOf(node->op());
129 if (CollectFeedbackInGenericLowering() && p.feedback().IsValid()) {
130 Callable callable = Builtins::CallableFor(isolate(), builtin_with_feedback);
131 Node* slot = jsgraph()->UintPtrConstant(p.feedback().slot.ToInt());
132 const CallInterfaceDescriptor& descriptor = callable.descriptor();
133 CallDescriptor::Flags flags = FrameStateFlagForCall(node);
134 auto call_descriptor = Linkage::GetStubCallDescriptor(
135 zone(), descriptor, descriptor.GetStackParameterCount(), flags,
136 node->op()->properties());
137 Node* stub_code = jsgraph()->HeapConstant(callable.code());
138 STATIC_ASSERT(JSUnaryOpNode::ValueIndex() == 0);
139 STATIC_ASSERT(JSUnaryOpNode::FeedbackVectorIndex() == 1);
140 DCHECK_EQ(node->op()->ValueInputCount(), 2);
141 node->InsertInput(zone(), 0, stub_code);
142 node->InsertInput(zone(), 2, slot);
143 NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
144 } else {
145 node->RemoveInput(JSUnaryOpNode::FeedbackVectorIndex());
146 ReplaceWithBuiltinCall(node, builtin_without_feedback);
147 }
148 }
149
150 #define DEF_UNARY_LOWERING(Name) \
151 void JSGenericLowering::LowerJS##Name(Node* node) { \
152 ReplaceUnaryOpWithBuiltinCall(node, Builtin::k##Name, \
153 Builtin::k##Name##_WithFeedback); \
154 }
155 DEF_UNARY_LOWERING(BitwiseNot)
DEF_UNARY_LOWERING(Decrement)156 DEF_UNARY_LOWERING(Decrement)
157 DEF_UNARY_LOWERING(Increment)
158 DEF_UNARY_LOWERING(Negate)
159 #undef DEF_UNARY_LOWERING
160
161 void JSGenericLowering::ReplaceBinaryOpWithBuiltinCall(
162 Node* node, Builtin builtin_without_feedback,
163 Builtin builtin_with_feedback) {
164 DCHECK(JSOperator::IsBinaryWithFeedback(node->opcode()));
165 Builtin builtin;
166 const FeedbackParameter& p = FeedbackParameterOf(node->op());
167 if (CollectFeedbackInGenericLowering() && p.feedback().IsValid()) {
168 Node* slot = jsgraph()->UintPtrConstant(p.feedback().slot.ToInt());
169 STATIC_ASSERT(JSBinaryOpNode::LeftIndex() == 0);
170 STATIC_ASSERT(JSBinaryOpNode::RightIndex() == 1);
171 STATIC_ASSERT(JSBinaryOpNode::FeedbackVectorIndex() == 2);
172 DCHECK_EQ(node->op()->ValueInputCount(), 3);
173 node->InsertInput(zone(), 2, slot);
174 builtin = builtin_with_feedback;
175 } else {
176 node->RemoveInput(JSBinaryOpNode::FeedbackVectorIndex());
177 builtin = builtin_without_feedback;
178 }
179
180 ReplaceWithBuiltinCall(node, builtin);
181 }
182
183 #define DEF_BINARY_LOWERING(Name) \
184 void JSGenericLowering::LowerJS##Name(Node* node) { \
185 ReplaceBinaryOpWithBuiltinCall(node, Builtin::k##Name, \
186 Builtin::k##Name##_WithFeedback); \
187 }
188 // Binary ops.
189 DEF_BINARY_LOWERING(Add)
DEF_BINARY_LOWERING(BitwiseAnd)190 DEF_BINARY_LOWERING(BitwiseAnd)
191 DEF_BINARY_LOWERING(BitwiseOr)
192 DEF_BINARY_LOWERING(BitwiseXor)
193 DEF_BINARY_LOWERING(Divide)
194 DEF_BINARY_LOWERING(Exponentiate)
195 DEF_BINARY_LOWERING(Modulus)
196 DEF_BINARY_LOWERING(Multiply)
197 DEF_BINARY_LOWERING(ShiftLeft)
198 DEF_BINARY_LOWERING(ShiftRight)
199 DEF_BINARY_LOWERING(ShiftRightLogical)
200 DEF_BINARY_LOWERING(Subtract)
201 // Compare ops.
202 DEF_BINARY_LOWERING(Equal)
203 DEF_BINARY_LOWERING(GreaterThan)
204 DEF_BINARY_LOWERING(GreaterThanOrEqual)
205 DEF_BINARY_LOWERING(InstanceOf)
206 DEF_BINARY_LOWERING(LessThan)
207 DEF_BINARY_LOWERING(LessThanOrEqual)
208 #undef DEF_BINARY_LOWERING
209
210 void JSGenericLowering::LowerJSStrictEqual(Node* node) {
211 // The === operator doesn't need the current context.
212 NodeProperties::ReplaceContextInput(node, jsgraph()->NoContextConstant());
213 DCHECK_EQ(node->op()->ControlInputCount(), 1);
214 node->RemoveInput(NodeProperties::FirstControlIndex(node));
215
216 Builtin builtin;
217 const FeedbackParameter& p = FeedbackParameterOf(node->op());
218 if (CollectFeedbackInGenericLowering() && p.feedback().IsValid()) {
219 Node* slot = jsgraph()->UintPtrConstant(p.feedback().slot.ToInt());
220 STATIC_ASSERT(JSStrictEqualNode::LeftIndex() == 0);
221 STATIC_ASSERT(JSStrictEqualNode::RightIndex() == 1);
222 STATIC_ASSERT(JSStrictEqualNode::FeedbackVectorIndex() == 2);
223 DCHECK_EQ(node->op()->ValueInputCount(), 3);
224 node->InsertInput(zone(), 2, slot);
225 builtin = Builtin::kStrictEqual_WithFeedback;
226 } else {
227 node->RemoveInput(JSStrictEqualNode::FeedbackVectorIndex());
228 builtin = Builtin::kStrictEqual;
229 }
230
231 Callable callable = Builtins::CallableFor(isolate(), builtin);
232 ReplaceWithBuiltinCall(node, callable, CallDescriptor::kNoFlags,
233 Operator::kEliminatable);
234 }
235
236 namespace {
237
238 // The megamorphic load builtin can be used as a performance optimization in
239 // some cases - unlike the full builtin, the megamorphic builtin does fewer
240 // checks and does not collect feedback.
ShouldUseMegamorphicLoadBuiltin(FeedbackSource const & source,base::Optional<NameRef> name,JSHeapBroker * broker)241 bool ShouldUseMegamorphicLoadBuiltin(FeedbackSource const& source,
242 base::Optional<NameRef> name,
243 JSHeapBroker* broker) {
244 ProcessedFeedback const& feedback =
245 broker->GetFeedbackForPropertyAccess(source, AccessMode::kLoad, name);
246
247 if (feedback.kind() == ProcessedFeedback::kElementAccess) {
248 return feedback.AsElementAccess().transition_groups().empty();
249 } else if (feedback.kind() == ProcessedFeedback::kNamedAccess) {
250 return feedback.AsNamedAccess().maps().empty();
251 } else if (feedback.kind() == ProcessedFeedback::kInsufficient) {
252 return false;
253 }
254 UNREACHABLE();
255 }
256
257 } // namespace
258
LowerJSHasProperty(Node * node)259 void JSGenericLowering::LowerJSHasProperty(Node* node) {
260 JSHasPropertyNode n(node);
261 const PropertyAccess& p = n.Parameters();
262 if (!p.feedback().IsValid()) {
263 node->RemoveInput(JSHasPropertyNode::FeedbackVectorIndex());
264 ReplaceWithBuiltinCall(node, Builtin::kHasProperty);
265 } else {
266 STATIC_ASSERT(n.FeedbackVectorIndex() == 2);
267 n->InsertInput(zone(), 2,
268 jsgraph()->TaggedIndexConstant(p.feedback().index()));
269 ReplaceWithBuiltinCall(node, Builtin::kKeyedHasIC);
270 }
271 }
272
LowerJSLoadProperty(Node * node)273 void JSGenericLowering::LowerJSLoadProperty(Node* node) {
274 JSLoadPropertyNode n(node);
275 const PropertyAccess& p = n.Parameters();
276 FrameState frame_state = n.frame_state();
277 Node* outer_state = frame_state.outer_frame_state();
278 STATIC_ASSERT(n.FeedbackVectorIndex() == 2);
279 if (outer_state->opcode() != IrOpcode::kFrameState) {
280 n->RemoveInput(n.FeedbackVectorIndex());
281 n->InsertInput(zone(), 2,
282 jsgraph()->TaggedIndexConstant(p.feedback().index()));
283 ReplaceWithBuiltinCall(
284 node, ShouldUseMegamorphicLoadBuiltin(p.feedback(), {}, broker())
285 ? Builtin::kKeyedLoadICTrampoline_Megamorphic
286 : Builtin::kKeyedLoadICTrampoline);
287 } else {
288 n->InsertInput(zone(), 2,
289 jsgraph()->TaggedIndexConstant(p.feedback().index()));
290 ReplaceWithBuiltinCall(
291 node, ShouldUseMegamorphicLoadBuiltin(p.feedback(), {}, broker())
292 ? Builtin::kKeyedLoadIC_Megamorphic
293 : Builtin::kKeyedLoadIC);
294 }
295 }
296
LowerJSLoadNamed(Node * node)297 void JSGenericLowering::LowerJSLoadNamed(Node* node) {
298 JSLoadNamedNode n(node);
299 NamedAccess const& p = n.Parameters();
300 FrameState frame_state = n.frame_state();
301 Node* outer_state = frame_state.outer_frame_state();
302 STATIC_ASSERT(n.FeedbackVectorIndex() == 1);
303 if (!p.feedback().IsValid()) {
304 n->RemoveInput(n.FeedbackVectorIndex());
305 node->InsertInput(zone(), 1, jsgraph()->Constant(p.name(broker())));
306 ReplaceWithBuiltinCall(node, Builtin::kGetProperty);
307 } else if (outer_state->opcode() != IrOpcode::kFrameState) {
308 n->RemoveInput(n.FeedbackVectorIndex());
309 node->InsertInput(zone(), 1, jsgraph()->Constant(p.name(broker())));
310 node->InsertInput(zone(), 2,
311 jsgraph()->TaggedIndexConstant(p.feedback().index()));
312 ReplaceWithBuiltinCall(node, ShouldUseMegamorphicLoadBuiltin(
313 p.feedback(), p.name(broker()), broker())
314 ? Builtin::kLoadICTrampoline_Megamorphic
315 : Builtin::kLoadICTrampoline);
316 } else {
317 node->InsertInput(zone(), 1, jsgraph()->Constant(p.name(broker())));
318 node->InsertInput(zone(), 2,
319 jsgraph()->TaggedIndexConstant(p.feedback().index()));
320 ReplaceWithBuiltinCall(node, ShouldUseMegamorphicLoadBuiltin(
321 p.feedback(), p.name(broker()), broker())
322 ? Builtin::kLoadIC_Megamorphic
323 : Builtin::kLoadIC);
324 }
325 }
326
LowerJSLoadNamedFromSuper(Node * node)327 void JSGenericLowering::LowerJSLoadNamedFromSuper(Node* node) {
328 JSLoadNamedFromSuperNode n(node);
329 NamedAccess const& p = n.Parameters();
330 Node* effect = NodeProperties::GetEffectInput(node);
331 Node* control = NodeProperties::GetControlInput(node);
332 // Node inputs: receiver, home object, FeedbackVector.
333 // LoadSuperIC expects: receiver, lookup start object, name, slot,
334 // FeedbackVector.
335 Node* home_object_map = effect = graph()->NewNode(
336 jsgraph()->simplified()->LoadField(AccessBuilder::ForMap()),
337 n.home_object(), effect, control);
338 Node* home_object_proto = effect = graph()->NewNode(
339 jsgraph()->simplified()->LoadField(AccessBuilder::ForMapPrototype()),
340 home_object_map, effect, control);
341 n->ReplaceInput(n.HomeObjectIndex(), home_object_proto);
342 NodeProperties::ReplaceEffectInput(node, effect);
343 STATIC_ASSERT(n.FeedbackVectorIndex() == 2);
344 // If the code below will be used for the invalid feedback case, it needs to
345 // be double-checked that the FeedbackVector parameter will be the
346 // UndefinedConstant.
347 DCHECK(p.feedback().IsValid());
348 node->InsertInput(zone(), 2, jsgraph()->Constant(p.name(broker())));
349 node->InsertInput(zone(), 3,
350 jsgraph()->TaggedIndexConstant(p.feedback().index()));
351 ReplaceWithBuiltinCall(node, Builtin::kLoadSuperIC);
352 }
353
LowerJSLoadGlobal(Node * node)354 void JSGenericLowering::LowerJSLoadGlobal(Node* node) {
355 JSLoadGlobalNode n(node);
356 const LoadGlobalParameters& p = n.Parameters();
357 CallDescriptor::Flags flags = FrameStateFlagForCall(node);
358 FrameState frame_state = n.frame_state();
359 Node* outer_state = frame_state.outer_frame_state();
360 STATIC_ASSERT(n.FeedbackVectorIndex() == 0);
361 if (outer_state->opcode() != IrOpcode::kFrameState) {
362 n->RemoveInput(n.FeedbackVectorIndex());
363 node->InsertInput(zone(), 0, jsgraph()->Constant(p.name(broker())));
364 node->InsertInput(zone(), 1,
365 jsgraph()->TaggedIndexConstant(p.feedback().index()));
366 Callable callable = CodeFactory::LoadGlobalIC(isolate(), p.typeof_mode());
367 ReplaceWithBuiltinCall(node, callable, flags);
368 } else {
369 node->InsertInput(zone(), 0, jsgraph()->Constant(p.name(broker())));
370 node->InsertInput(zone(), 1,
371 jsgraph()->TaggedIndexConstant(p.feedback().index()));
372 Callable callable =
373 CodeFactory::LoadGlobalICInOptimizedCode(isolate(), p.typeof_mode());
374 ReplaceWithBuiltinCall(node, callable, flags);
375 }
376 }
377
LowerJSGetIterator(Node * node)378 void JSGenericLowering::LowerJSGetIterator(Node* node) {
379 // TODO(v8:9625): Currently, the GetIterator operator is desugared in the
380 // native context specialization phase. Thus, the following generic lowering
381 // is not reachable unless that phase is disabled (e.g. for
382 // native-context-independent code).
383 // We can add a check in native context specialization to avoid desugaring
384 // the GetIterator operator when feedback is megamorphic. This would reduce
385 // the size of the compiled code as it would insert 1 call to the builtin
386 // instead of 2 calls resulting from the generic lowering of the LoadNamed
387 // and Call operators.
388
389 JSGetIteratorNode n(node);
390 GetIteratorParameters const& p = n.Parameters();
391 Node* load_slot =
392 jsgraph()->TaggedIndexConstant(p.loadFeedback().slot.ToInt());
393 Node* call_slot =
394 jsgraph()->TaggedIndexConstant(p.callFeedback().slot.ToInt());
395 STATIC_ASSERT(n.FeedbackVectorIndex() == 1);
396 node->InsertInput(zone(), 1, load_slot);
397 node->InsertInput(zone(), 2, call_slot);
398
399 ReplaceWithBuiltinCall(node, Builtin::kGetIteratorWithFeedback);
400 }
401
LowerJSSetKeyedProperty(Node * node)402 void JSGenericLowering::LowerJSSetKeyedProperty(Node* node) {
403 JSSetKeyedPropertyNode n(node);
404 const PropertyAccess& p = n.Parameters();
405 FrameState frame_state = n.frame_state();
406 Node* outer_state = frame_state.outer_frame_state();
407 STATIC_ASSERT(n.FeedbackVectorIndex() == 3);
408 if (outer_state->opcode() != IrOpcode::kFrameState) {
409 n->RemoveInput(n.FeedbackVectorIndex());
410 node->InsertInput(zone(), 3,
411 jsgraph()->TaggedIndexConstant(p.feedback().index()));
412
413 // KeyedStoreIC is currently a base class for multiple keyed property store
414 // operations and contains mixed logic for set and define operations,
415 // the paths are controlled by feedback.
416 // TODO(v8:12548): refactor SetKeyedIC as a subclass of KeyedStoreIC, which
417 // can be called here.
418 ReplaceWithBuiltinCall(node, Builtin::kKeyedStoreICTrampoline);
419 } else {
420 node->InsertInput(zone(), 3,
421 jsgraph()->TaggedIndexConstant(p.feedback().index()));
422 ReplaceWithBuiltinCall(node, Builtin::kKeyedStoreIC);
423 }
424 }
425
LowerJSDefineKeyedOwnProperty(Node * node)426 void JSGenericLowering::LowerJSDefineKeyedOwnProperty(Node* node) {
427 JSDefineKeyedOwnPropertyNode n(node);
428 const PropertyAccess& p = n.Parameters();
429 FrameState frame_state = n.frame_state();
430 Node* outer_state = frame_state.outer_frame_state();
431 STATIC_ASSERT(n.FeedbackVectorIndex() == 3);
432 if (outer_state->opcode() != IrOpcode::kFrameState) {
433 n->RemoveInput(n.FeedbackVectorIndex());
434 node->InsertInput(zone(), 3,
435 jsgraph()->TaggedIndexConstant(p.feedback().index()));
436 ReplaceWithBuiltinCall(node, Builtin::kDefineKeyedOwnICTrampoline);
437 } else {
438 node->InsertInput(zone(), 3,
439 jsgraph()->TaggedIndexConstant(p.feedback().index()));
440 ReplaceWithBuiltinCall(node, Builtin::kDefineKeyedOwnIC);
441 }
442 }
443
LowerJSSetNamedProperty(Node * node)444 void JSGenericLowering::LowerJSSetNamedProperty(Node* node) {
445 JSSetNamedPropertyNode n(node);
446 NamedAccess const& p = n.Parameters();
447 FrameState frame_state = n.frame_state();
448 Node* outer_state = frame_state.outer_frame_state();
449 STATIC_ASSERT(n.FeedbackVectorIndex() == 2);
450 if (!p.feedback().IsValid()) {
451 n->RemoveInput(n.FeedbackVectorIndex());
452 node->InsertInput(zone(), 1, jsgraph()->Constant(p.name(broker())));
453 ReplaceWithRuntimeCall(node, Runtime::kSetNamedProperty);
454 } else if (outer_state->opcode() != IrOpcode::kFrameState) {
455 n->RemoveInput(n.FeedbackVectorIndex());
456 node->InsertInput(zone(), 1, jsgraph()->Constant(p.name(broker())));
457 node->InsertInput(zone(), 3,
458 jsgraph()->TaggedIndexConstant(p.feedback().index()));
459 // StoreIC is currently a base class for multiple property store operations
460 // and contains mixed logic for named and keyed, set and define operations,
461 // the paths are controlled by feedback.
462 // TODO(v8:12548): refactor SetNamedIC as a subclass of StoreIC, which can
463 // be called here.
464 ReplaceWithBuiltinCall(node, Builtin::kStoreICTrampoline);
465 } else {
466 node->InsertInput(zone(), 1, jsgraph()->Constant(p.name(broker())));
467 node->InsertInput(zone(), 3,
468 jsgraph()->TaggedIndexConstant(p.feedback().index()));
469 ReplaceWithBuiltinCall(node, Builtin::kStoreIC);
470 }
471 }
472
LowerJSDefineNamedOwnProperty(Node * node)473 void JSGenericLowering::LowerJSDefineNamedOwnProperty(Node* node) {
474 CallDescriptor::Flags flags = FrameStateFlagForCall(node);
475 JSDefineNamedOwnPropertyNode n(node);
476 DefineNamedOwnPropertyParameters const& p = n.Parameters();
477 FrameState frame_state = n.frame_state();
478 Node* outer_state = frame_state.outer_frame_state();
479 STATIC_ASSERT(n.FeedbackVectorIndex() == 2);
480 if (outer_state->opcode() != IrOpcode::kFrameState) {
481 n->RemoveInput(n.FeedbackVectorIndex());
482 node->InsertInput(zone(), 1, jsgraph()->Constant(p.name(broker())));
483 node->InsertInput(zone(), 3,
484 jsgraph()->TaggedIndexConstant(p.feedback().index()));
485 Callable callable = CodeFactory::DefineNamedOwnIC(isolate());
486 ReplaceWithBuiltinCall(node, callable, flags);
487 } else {
488 node->InsertInput(zone(), 1, jsgraph()->Constant(p.name(broker())));
489 node->InsertInput(zone(), 3,
490 jsgraph()->TaggedIndexConstant(p.feedback().index()));
491 Callable callable = CodeFactory::DefineNamedOwnICInOptimizedCode(isolate());
492 ReplaceWithBuiltinCall(node, callable, flags);
493 }
494 }
495
LowerJSStoreGlobal(Node * node)496 void JSGenericLowering::LowerJSStoreGlobal(Node* node) {
497 JSStoreGlobalNode n(node);
498 const StoreGlobalParameters& p = n.Parameters();
499 FrameState frame_state = n.frame_state();
500 Node* outer_state = frame_state.outer_frame_state();
501 STATIC_ASSERT(n.FeedbackVectorIndex() == 1);
502 if (outer_state->opcode() != IrOpcode::kFrameState) {
503 n->RemoveInput(n.FeedbackVectorIndex());
504 node->InsertInput(zone(), 0, jsgraph()->Constant(p.name(broker())));
505 node->InsertInput(zone(), 2,
506 jsgraph()->TaggedIndexConstant(p.feedback().index()));
507 ReplaceWithBuiltinCall(node, Builtin::kStoreGlobalICTrampoline);
508 } else {
509 node->InsertInput(zone(), 0, jsgraph()->Constant(p.name(broker())));
510 node->InsertInput(zone(), 2,
511 jsgraph()->TaggedIndexConstant(p.feedback().index()));
512 ReplaceWithBuiltinCall(node, Builtin::kStoreGlobalIC);
513 }
514 }
515
LowerJSDefineKeyedOwnPropertyInLiteral(Node * node)516 void JSGenericLowering::LowerJSDefineKeyedOwnPropertyInLiteral(Node* node) {
517 JSDefineKeyedOwnPropertyInLiteralNode n(node);
518 FeedbackParameter const& p = n.Parameters();
519 STATIC_ASSERT(n.FeedbackVectorIndex() == 4);
520 RelaxControls(node);
521 node->InsertInput(zone(), 5,
522 jsgraph()->TaggedIndexConstant(p.feedback().index()));
523 ReplaceWithRuntimeCall(node, Runtime::kDefineKeyedOwnPropertyInLiteral);
524 }
525
LowerJSStoreInArrayLiteral(Node * node)526 void JSGenericLowering::LowerJSStoreInArrayLiteral(Node* node) {
527 JSStoreInArrayLiteralNode n(node);
528 FeedbackParameter const& p = n.Parameters();
529 STATIC_ASSERT(n.FeedbackVectorIndex() == 3);
530 RelaxControls(node);
531 node->InsertInput(zone(), 3,
532 jsgraph()->TaggedIndexConstant(p.feedback().index()));
533 ReplaceWithBuiltinCall(node, Builtin::kStoreInArrayLiteralIC);
534 }
535
LowerJSDeleteProperty(Node * node)536 void JSGenericLowering::LowerJSDeleteProperty(Node* node) {
537 ReplaceWithBuiltinCall(node, Builtin::kDeleteProperty);
538 }
539
LowerJSGetSuperConstructor(Node * node)540 void JSGenericLowering::LowerJSGetSuperConstructor(Node* node) {
541 Node* active_function = NodeProperties::GetValueInput(node, 0);
542 Node* effect = NodeProperties::GetEffectInput(node);
543 Node* control = NodeProperties::GetControlInput(node);
544
545 Node* function_map = effect = graph()->NewNode(
546 jsgraph()->simplified()->LoadField(AccessBuilder::ForMap()),
547 active_function, effect, control);
548
549 RelaxControls(node);
550 node->ReplaceInput(0, function_map);
551 node->ReplaceInput(1, effect);
552 node->ReplaceInput(2, control);
553 node->TrimInputCount(3);
554 NodeProperties::ChangeOp(node, jsgraph()->simplified()->LoadField(
555 AccessBuilder::ForMapPrototype()));
556 }
557
LowerJSHasInPrototypeChain(Node * node)558 void JSGenericLowering::LowerJSHasInPrototypeChain(Node* node) {
559 ReplaceWithRuntimeCall(node, Runtime::kHasInPrototypeChain);
560 }
561
LowerJSOrdinaryHasInstance(Node * node)562 void JSGenericLowering::LowerJSOrdinaryHasInstance(Node* node) {
563 ReplaceWithBuiltinCall(node, Builtin::kOrdinaryHasInstance);
564 }
565
LowerJSHasContextExtension(Node * node)566 void JSGenericLowering::LowerJSHasContextExtension(Node* node) {
567 UNREACHABLE(); // Eliminated in typed lowering.
568 }
569
LowerJSLoadContext(Node * node)570 void JSGenericLowering::LowerJSLoadContext(Node* node) {
571 UNREACHABLE(); // Eliminated in typed lowering.
572 }
573
574
LowerJSStoreContext(Node * node)575 void JSGenericLowering::LowerJSStoreContext(Node* node) {
576 UNREACHABLE(); // Eliminated in typed lowering.
577 }
578
579
LowerJSCreate(Node * node)580 void JSGenericLowering::LowerJSCreate(Node* node) {
581 ReplaceWithBuiltinCall(node, Builtin::kFastNewObject);
582 }
583
584
LowerJSCreateArguments(Node * node)585 void JSGenericLowering::LowerJSCreateArguments(Node* node) {
586 CreateArgumentsType const type = CreateArgumentsTypeOf(node->op());
587 switch (type) {
588 case CreateArgumentsType::kMappedArguments:
589 ReplaceWithRuntimeCall(node, Runtime::kNewSloppyArguments);
590 break;
591 case CreateArgumentsType::kUnmappedArguments:
592 ReplaceWithRuntimeCall(node, Runtime::kNewStrictArguments);
593 break;
594 case CreateArgumentsType::kRestParameter:
595 ReplaceWithRuntimeCall(node, Runtime::kNewRestParameter);
596 break;
597 }
598 }
599
600
LowerJSCreateArray(Node * node)601 void JSGenericLowering::LowerJSCreateArray(Node* node) {
602 CreateArrayParameters const& p = CreateArrayParametersOf(node->op());
603 int const arity = static_cast<int>(p.arity());
604 auto interface_descriptor = ArrayConstructorDescriptor{};
605 auto call_descriptor = Linkage::GetStubCallDescriptor(
606 zone(), interface_descriptor, arity + 1, CallDescriptor::kNeedsFrameState,
607 node->op()->properties());
608 // If this fails, we might need to update the parameter reordering code
609 // to ensure that the additional arguments passed via stack are pushed
610 // between top of stack and JS arguments.
611 DCHECK_EQ(interface_descriptor.GetStackParameterCount(), 0);
612 Node* stub_code = jsgraph()->ArrayConstructorStubConstant();
613 Node* stub_arity = jsgraph()->Int32Constant(JSParameterCount(arity));
614 base::Optional<AllocationSiteRef> const site = p.site(broker());
615 Node* type_info = site.has_value() ? jsgraph()->Constant(site.value())
616 : jsgraph()->UndefinedConstant();
617 Node* receiver = jsgraph()->UndefinedConstant();
618 node->InsertInput(zone(), 0, stub_code);
619 node->InsertInput(zone(), 3, stub_arity);
620 node->InsertInput(zone(), 4, type_info);
621 node->InsertInput(zone(), 5, receiver);
622 NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
623 }
624
LowerJSCreateArrayIterator(Node * node)625 void JSGenericLowering::LowerJSCreateArrayIterator(Node* node) {
626 UNREACHABLE(); // Eliminated in typed lowering.
627 }
628
LowerJSCreateAsyncFunctionObject(Node * node)629 void JSGenericLowering::LowerJSCreateAsyncFunctionObject(Node* node) {
630 UNREACHABLE(); // Eliminated in typed lowering.
631 }
632
LowerJSCreateCollectionIterator(Node * node)633 void JSGenericLowering::LowerJSCreateCollectionIterator(Node* node) {
634 UNREACHABLE(); // Eliminated in typed lowering.
635 }
636
LowerJSCreateBoundFunction(Node * node)637 void JSGenericLowering::LowerJSCreateBoundFunction(Node* node) {
638 UNREACHABLE(); // Eliminated in typed lowering.
639 }
640
LowerJSObjectIsArray(Node * node)641 void JSGenericLowering::LowerJSObjectIsArray(Node* node) {
642 UNREACHABLE(); // Eliminated in typed lowering.
643 }
644
LowerJSCreateObject(Node * node)645 void JSGenericLowering::LowerJSCreateObject(Node* node) {
646 ReplaceWithBuiltinCall(node, Builtin::kCreateObjectWithoutProperties);
647 }
648
LowerJSParseInt(Node * node)649 void JSGenericLowering::LowerJSParseInt(Node* node) {
650 ReplaceWithBuiltinCall(node, Builtin::kParseInt);
651 }
652
LowerJSRegExpTest(Node * node)653 void JSGenericLowering::LowerJSRegExpTest(Node* node) {
654 ReplaceWithBuiltinCall(node, Builtin::kRegExpPrototypeTestFast);
655 }
656
LowerJSCreateClosure(Node * node)657 void JSGenericLowering::LowerJSCreateClosure(Node* node) {
658 JSCreateClosureNode n(node);
659 CreateClosureParameters const& p = n.Parameters();
660 SharedFunctionInfoRef shared_info = p.shared_info(broker());
661 STATIC_ASSERT(n.FeedbackCellIndex() == 0);
662 node->InsertInput(zone(), 0, jsgraph()->Constant(shared_info));
663 node->RemoveInput(4); // control
664
665 // Use the FastNewClosure builtin only for functions allocated in new space.
666 if (p.allocation() == AllocationType::kYoung) {
667 ReplaceWithBuiltinCall(node, Builtin::kFastNewClosure);
668 } else {
669 ReplaceWithRuntimeCall(node, Runtime::kNewClosure_Tenured);
670 }
671 }
672
LowerJSCreateFunctionContext(Node * node)673 void JSGenericLowering::LowerJSCreateFunctionContext(Node* node) {
674 const CreateFunctionContextParameters& parameters =
675 CreateFunctionContextParametersOf(node->op());
676 ScopeInfoRef scope_info = parameters.scope_info(broker());
677 int slot_count = parameters.slot_count();
678 ScopeType scope_type = parameters.scope_type();
679 CallDescriptor::Flags flags = FrameStateFlagForCall(node);
680
681 if (slot_count <= ConstructorBuiltins::MaximumFunctionContextSlots()) {
682 Callable callable =
683 CodeFactory::FastNewFunctionContext(isolate(), scope_type);
684 node->InsertInput(zone(), 0, jsgraph()->Constant(scope_info));
685 node->InsertInput(zone(), 1, jsgraph()->Int32Constant(slot_count));
686 ReplaceWithBuiltinCall(node, callable, flags);
687 } else {
688 node->InsertInput(zone(), 0, jsgraph()->Constant(scope_info));
689 ReplaceWithRuntimeCall(node, Runtime::kNewFunctionContext);
690 }
691 }
692
LowerJSCreateGeneratorObject(Node * node)693 void JSGenericLowering::LowerJSCreateGeneratorObject(Node* node) {
694 node->RemoveInput(4); // control
695 ReplaceWithBuiltinCall(node, Builtin::kCreateGeneratorObject);
696 }
697
LowerJSCreateIterResultObject(Node * node)698 void JSGenericLowering::LowerJSCreateIterResultObject(Node* node) {
699 ReplaceWithBuiltinCall(node, Builtin::kCreateIterResultObject);
700 }
701
LowerJSCreateStringIterator(Node * node)702 void JSGenericLowering::LowerJSCreateStringIterator(Node* node) {
703 UNREACHABLE(); // Eliminated in typed lowering.
704 }
705
LowerJSCreateKeyValueArray(Node * node)706 void JSGenericLowering::LowerJSCreateKeyValueArray(Node* node) {
707 UNREACHABLE(); // Eliminated in typed lowering.
708 }
709
LowerJSCreatePromise(Node * node)710 void JSGenericLowering::LowerJSCreatePromise(Node* node) {
711 UNREACHABLE(); // Eliminated in typed lowering.
712 }
713
LowerJSCreateTypedArray(Node * node)714 void JSGenericLowering::LowerJSCreateTypedArray(Node* node) {
715 ReplaceWithBuiltinCall(node, Builtin::kCreateTypedArray);
716 }
717
LowerJSCreateLiteralArray(Node * node)718 void JSGenericLowering::LowerJSCreateLiteralArray(Node* node) {
719 JSCreateLiteralArrayNode n(node);
720 CreateLiteralParameters const& p = n.Parameters();
721 STATIC_ASSERT(n.FeedbackVectorIndex() == 0);
722 node->InsertInput(zone(), 1,
723 jsgraph()->TaggedIndexConstant(p.feedback().index()));
724 node->InsertInput(zone(), 2, jsgraph()->Constant(p.constant(broker())));
725 node->InsertInput(zone(), 3, jsgraph()->SmiConstant(p.flags()));
726
727 // Use the CreateShallowArrayLiteral builtin only for shallow boilerplates
728 // without properties up to the number of elements that the stubs can handle.
729 if ((p.flags() & AggregateLiteral::kIsShallow) != 0 &&
730 p.length() < ConstructorBuiltins::kMaximumClonedShallowArrayElements) {
731 ReplaceWithBuiltinCall(node, Builtin::kCreateShallowArrayLiteral);
732 } else {
733 ReplaceWithRuntimeCall(node, Runtime::kCreateArrayLiteral);
734 }
735 }
736
LowerJSGetTemplateObject(Node * node)737 void JSGenericLowering::LowerJSGetTemplateObject(Node* node) {
738 JSGetTemplateObjectNode n(node);
739 GetTemplateObjectParameters const& p = n.Parameters();
740 SharedFunctionInfoRef shared = p.shared(broker());
741 TemplateObjectDescriptionRef description = p.description(broker());
742
743 DCHECK_EQ(node->op()->ControlInputCount(), 1);
744 node->RemoveInput(NodeProperties::FirstControlIndex(node));
745
746 STATIC_ASSERT(JSGetTemplateObjectNode::FeedbackVectorIndex() == 0);
747 node->InsertInput(zone(), 0, jsgraph()->Constant(shared));
748 node->InsertInput(zone(), 1, jsgraph()->Constant(description));
749 node->InsertInput(zone(), 2,
750 jsgraph()->UintPtrConstant(p.feedback().index()));
751
752 ReplaceWithBuiltinCall(node, Builtin::kGetTemplateObject);
753 }
754
LowerJSCreateEmptyLiteralArray(Node * node)755 void JSGenericLowering::LowerJSCreateEmptyLiteralArray(Node* node) {
756 JSCreateEmptyLiteralArrayNode n(node);
757 FeedbackParameter const& p = n.Parameters();
758 STATIC_ASSERT(n.FeedbackVectorIndex() == 0);
759 node->InsertInput(zone(), 1,
760 jsgraph()->TaggedIndexConstant(p.feedback().index()));
761 node->RemoveInput(4); // control
762 ReplaceWithBuiltinCall(node, Builtin::kCreateEmptyArrayLiteral);
763 }
764
LowerJSCreateArrayFromIterable(Node * node)765 void JSGenericLowering::LowerJSCreateArrayFromIterable(Node* node) {
766 ReplaceWithBuiltinCall(node, Builtin::kIterableToListWithSymbolLookup);
767 }
768
LowerJSCreateLiteralObject(Node * node)769 void JSGenericLowering::LowerJSCreateLiteralObject(Node* node) {
770 JSCreateLiteralObjectNode n(node);
771 CreateLiteralParameters const& p = n.Parameters();
772 STATIC_ASSERT(n.FeedbackVectorIndex() == 0);
773 node->InsertInput(zone(), 1,
774 jsgraph()->TaggedIndexConstant(p.feedback().index()));
775 node->InsertInput(zone(), 2, jsgraph()->Constant(p.constant(broker())));
776 node->InsertInput(zone(), 3, jsgraph()->SmiConstant(p.flags()));
777
778 // Use the CreateShallowObjectLiteratal builtin only for shallow boilerplates
779 // without elements up to the number of properties that the stubs can handle.
780 if ((p.flags() & AggregateLiteral::kIsShallow) != 0 &&
781 p.length() <=
782 ConstructorBuiltins::kMaximumClonedShallowObjectProperties) {
783 ReplaceWithBuiltinCall(node, Builtin::kCreateShallowObjectLiteral);
784 } else {
785 ReplaceWithRuntimeCall(node, Runtime::kCreateObjectLiteral);
786 }
787 }
788
LowerJSCloneObject(Node * node)789 void JSGenericLowering::LowerJSCloneObject(Node* node) {
790 JSCloneObjectNode n(node);
791 CloneObjectParameters const& p = n.Parameters();
792 STATIC_ASSERT(n.FeedbackVectorIndex() == 1);
793 node->InsertInput(zone(), 1, jsgraph()->SmiConstant(p.flags()));
794 node->InsertInput(zone(), 2,
795 jsgraph()->TaggedIndexConstant(p.feedback().index()));
796 ReplaceWithBuiltinCall(node, Builtin::kCloneObjectIC);
797 }
798
LowerJSCreateEmptyLiteralObject(Node * node)799 void JSGenericLowering::LowerJSCreateEmptyLiteralObject(Node* node) {
800 ReplaceWithBuiltinCall(node, Builtin::kCreateEmptyLiteralObject);
801 }
802
LowerJSCreateLiteralRegExp(Node * node)803 void JSGenericLowering::LowerJSCreateLiteralRegExp(Node* node) {
804 JSCreateLiteralRegExpNode n(node);
805 CreateLiteralParameters const& p = n.Parameters();
806 STATIC_ASSERT(n.FeedbackVectorIndex() == 0);
807 node->InsertInput(zone(), 1,
808 jsgraph()->TaggedIndexConstant(p.feedback().index()));
809 node->InsertInput(zone(), 2, jsgraph()->Constant(p.constant(broker())));
810 node->InsertInput(zone(), 3, jsgraph()->SmiConstant(p.flags()));
811 ReplaceWithBuiltinCall(node, Builtin::kCreateRegExpLiteral);
812 }
813
814
LowerJSCreateCatchContext(Node * node)815 void JSGenericLowering::LowerJSCreateCatchContext(Node* node) {
816 ScopeInfoRef scope_info = ScopeInfoOf(broker(), node->op());
817 node->InsertInput(zone(), 1, jsgraph()->Constant(scope_info));
818 ReplaceWithRuntimeCall(node, Runtime::kPushCatchContext);
819 }
820
LowerJSCreateWithContext(Node * node)821 void JSGenericLowering::LowerJSCreateWithContext(Node* node) {
822 ScopeInfoRef scope_info = ScopeInfoOf(broker(), node->op());
823 node->InsertInput(zone(), 1, jsgraph()->Constant(scope_info));
824 ReplaceWithRuntimeCall(node, Runtime::kPushWithContext);
825 }
826
LowerJSCreateBlockContext(Node * node)827 void JSGenericLowering::LowerJSCreateBlockContext(Node* node) {
828 ScopeInfoRef scope_info = ScopeInfoOf(broker(), node->op());
829 node->InsertInput(zone(), 0, jsgraph()->Constant(scope_info));
830 ReplaceWithRuntimeCall(node, Runtime::kPushBlockContext);
831 }
832
833 // TODO(jgruber,v8:8888): Should this collect feedback?
LowerJSConstructForwardVarargs(Node * node)834 void JSGenericLowering::LowerJSConstructForwardVarargs(Node* node) {
835 ConstructForwardVarargsParameters p =
836 ConstructForwardVarargsParametersOf(node->op());
837 int const arg_count = static_cast<int>(p.arity() - 2);
838 CallDescriptor::Flags flags = FrameStateFlagForCall(node);
839 Callable callable = CodeFactory::ConstructForwardVarargs(isolate());
840 // If this fails, we might need to update the parameter reordering code
841 // to ensure that the additional arguments passed via stack are pushed
842 // between top of stack and JS arguments.
843 DCHECK_EQ(callable.descriptor().GetStackParameterCount(), 0);
844 auto call_descriptor = Linkage::GetStubCallDescriptor(
845 zone(), callable.descriptor(), arg_count + 1, flags);
846 Node* stub_code = jsgraph()->HeapConstant(callable.code());
847 Node* stub_arity = jsgraph()->Int32Constant(JSParameterCount(arg_count));
848 Node* start_index = jsgraph()->Uint32Constant(p.start_index());
849 Node* receiver = jsgraph()->UndefinedConstant();
850 node->InsertInput(zone(), 0, stub_code);
851 node->InsertInput(zone(), 3, stub_arity);
852 node->InsertInput(zone(), 4, start_index);
853 node->InsertInput(zone(), 5, receiver);
854 NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
855 }
856
LowerJSConstruct(Node * node)857 void JSGenericLowering::LowerJSConstruct(Node* node) {
858 JSConstructNode n(node);
859 ConstructParameters const& p = n.Parameters();
860 int const arg_count = p.arity_without_implicit_args();
861 CallDescriptor::Flags flags = FrameStateFlagForCall(node);
862
863 static constexpr int kReceiver = 1;
864
865 const int stack_argument_count = arg_count + kReceiver;
866 Callable callable = Builtins::CallableFor(isolate(), Builtin::kConstruct);
867 auto call_descriptor = Linkage::GetStubCallDescriptor(
868 zone(), callable.descriptor(), stack_argument_count, flags);
869 Node* stub_code = jsgraph()->HeapConstant(callable.code());
870 Node* stub_arity = jsgraph()->Int32Constant(JSParameterCount(arg_count));
871 Node* receiver = jsgraph()->UndefinedConstant();
872 node->RemoveInput(n.FeedbackVectorIndex());
873 node->InsertInput(zone(), 0, stub_code);
874 node->InsertInput(zone(), 3, stub_arity);
875 node->InsertInput(zone(), 4, receiver);
876
877 // After: {code, target, new_target, arity, receiver, ...args}.
878
879 NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
880 }
881
LowerJSConstructWithArrayLike(Node * node)882 void JSGenericLowering::LowerJSConstructWithArrayLike(Node* node) {
883 JSConstructWithArrayLikeNode n(node);
884 ConstructParameters const& p = n.Parameters();
885 CallDescriptor::Flags flags = FrameStateFlagForCall(node);
886 const int arg_count = p.arity_without_implicit_args();
887 DCHECK_EQ(arg_count, 1);
888
889 static constexpr int kReceiver = 1;
890 static constexpr int kArgumentList = 1;
891
892 const int stack_argument_count = arg_count - kArgumentList + kReceiver;
893 Callable callable =
894 Builtins::CallableFor(isolate(), Builtin::kConstructWithArrayLike);
895 // If this fails, we might need to update the parameter reordering code
896 // to ensure that the additional arguments passed via stack are pushed
897 // between top of stack and JS arguments.
898 DCHECK_EQ(callable.descriptor().GetStackParameterCount(), 0);
899 auto call_descriptor = Linkage::GetStubCallDescriptor(
900 zone(), callable.descriptor(), stack_argument_count, flags);
901 Node* stub_code = jsgraph()->HeapConstant(callable.code());
902 Node* receiver = jsgraph()->UndefinedConstant();
903 node->RemoveInput(n.FeedbackVectorIndex());
904 node->InsertInput(zone(), 0, stub_code);
905 node->InsertInput(zone(), 4, receiver);
906
907 // After: {code, target, new_target, arguments_list, receiver}.
908
909 NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
910 }
911
LowerJSConstructWithSpread(Node * node)912 void JSGenericLowering::LowerJSConstructWithSpread(Node* node) {
913 JSConstructWithSpreadNode n(node);
914 ConstructParameters const& p = n.Parameters();
915 int const arg_count = p.arity_without_implicit_args();
916 DCHECK_GE(arg_count, 1);
917 CallDescriptor::Flags flags = FrameStateFlagForCall(node);
918
919 static constexpr int kReceiver = 1;
920 static constexpr int kTheSpread = 1; // Included in `arg_count`.
921
922 const int stack_argument_count = arg_count + kReceiver - kTheSpread;
923 Callable callable = CodeFactory::ConstructWithSpread(isolate());
924 // If this fails, we might need to update the parameter reordering code
925 // to ensure that the additional arguments passed via stack are pushed
926 // between top of stack and JS arguments.
927 DCHECK_EQ(callable.descriptor().GetStackParameterCount(), 0);
928 auto call_descriptor = Linkage::GetStubCallDescriptor(
929 zone(), callable.descriptor(), stack_argument_count, flags);
930 Node* stub_code = jsgraph()->HeapConstant(callable.code());
931
932 // We pass the spread in a register, not on the stack.
933 Node* stub_arity =
934 jsgraph()->Int32Constant(JSParameterCount(arg_count - kTheSpread));
935 Node* receiver = jsgraph()->UndefinedConstant();
936 DCHECK(n.FeedbackVectorIndex() > n.LastArgumentIndex());
937 node->RemoveInput(n.FeedbackVectorIndex());
938 Node* spread = node->RemoveInput(n.LastArgumentIndex());
939
940 node->InsertInput(zone(), 0, stub_code);
941 node->InsertInput(zone(), 3, stub_arity);
942 node->InsertInput(zone(), 4, spread);
943 node->InsertInput(zone(), 5, receiver);
944
945 // After: {code, target, new_target, arity, spread, receiver, ...args}.
946
947 NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
948 }
949
LowerJSCallForwardVarargs(Node * node)950 void JSGenericLowering::LowerJSCallForwardVarargs(Node* node) {
951 CallForwardVarargsParameters p = CallForwardVarargsParametersOf(node->op());
952 int const arg_count = static_cast<int>(p.arity() - 2);
953 CallDescriptor::Flags flags = FrameStateFlagForCall(node);
954 Callable callable = CodeFactory::CallForwardVarargs(isolate());
955 auto call_descriptor = Linkage::GetStubCallDescriptor(
956 zone(), callable.descriptor(), arg_count + 1, flags);
957 Node* stub_code = jsgraph()->HeapConstant(callable.code());
958 Node* stub_arity = jsgraph()->Int32Constant(JSParameterCount(arg_count));
959 Node* start_index = jsgraph()->Uint32Constant(p.start_index());
960 node->InsertInput(zone(), 0, stub_code);
961 node->InsertInput(zone(), 2, stub_arity);
962 node->InsertInput(zone(), 3, start_index);
963 NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
964 }
965
LowerJSCall(Node * node)966 void JSGenericLowering::LowerJSCall(Node* node) {
967 JSCallNode n(node);
968 CallParameters const& p = n.Parameters();
969 int const arg_count = p.arity_without_implicit_args();
970 ConvertReceiverMode const mode = p.convert_mode();
971
972 node->RemoveInput(n.FeedbackVectorIndex());
973
974 Callable callable = CodeFactory::Call(isolate(), mode);
975 CallDescriptor::Flags flags = FrameStateFlagForCall(node);
976 auto call_descriptor = Linkage::GetStubCallDescriptor(
977 zone(), callable.descriptor(), arg_count + 1, flags);
978 Node* stub_code = jsgraph()->HeapConstant(callable.code());
979 Node* stub_arity = jsgraph()->Int32Constant(JSParameterCount(arg_count));
980 node->InsertInput(zone(), 0, stub_code);
981 node->InsertInput(zone(), 2, stub_arity);
982 NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
983 }
984
LowerJSCallWithArrayLike(Node * node)985 void JSGenericLowering::LowerJSCallWithArrayLike(Node* node) {
986 JSCallWithArrayLikeNode n(node);
987 CallParameters const& p = n.Parameters();
988 const int arg_count = p.arity_without_implicit_args();
989 DCHECK_EQ(arg_count, 1); // The arraylike object.
990 CallDescriptor::Flags flags = FrameStateFlagForCall(node);
991
992 static constexpr int kArgumentsList = 1;
993 static constexpr int kReceiver = 1;
994
995 const int stack_argument_count = arg_count - kArgumentsList + kReceiver;
996 Callable callable = CodeFactory::CallWithArrayLike(isolate());
997 auto call_descriptor = Linkage::GetStubCallDescriptor(
998 zone(), callable.descriptor(), stack_argument_count, flags);
999 Node* stub_code = jsgraph()->HeapConstant(callable.code());
1000 Node* receiver = n.receiver();
1001 Node* arguments_list = n.Argument(0);
1002
1003 // Shuffling inputs.
1004 // Before: {target, receiver, arguments_list, vector}.
1005
1006 node->RemoveInput(n.FeedbackVectorIndex());
1007 node->InsertInput(zone(), 0, stub_code);
1008 node->ReplaceInput(2, arguments_list);
1009 node->ReplaceInput(3, receiver);
1010
1011 // After: {code, target, arguments_list, receiver}.
1012
1013 NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
1014 }
1015
LowerJSCallWithSpread(Node * node)1016 void JSGenericLowering::LowerJSCallWithSpread(Node* node) {
1017 JSCallWithSpreadNode n(node);
1018 CallParameters const& p = n.Parameters();
1019 int const arg_count = p.arity_without_implicit_args();
1020 DCHECK_GE(arg_count, 1); // At least the spread.
1021 CallDescriptor::Flags flags = FrameStateFlagForCall(node);
1022
1023 static constexpr int kReceiver = 1;
1024 static constexpr int kTheSpread = 1;
1025
1026 const int stack_argument_count = arg_count - kTheSpread + kReceiver;
1027 Callable callable = CodeFactory::CallWithSpread(isolate());
1028 // If this fails, we might need to update the parameter reordering code
1029 // to ensure that the additional arguments passed via stack are pushed
1030 // between top of stack and JS arguments.
1031 DCHECK_EQ(callable.descriptor().GetStackParameterCount(), 0);
1032 auto call_descriptor = Linkage::GetStubCallDescriptor(
1033 zone(), callable.descriptor(), stack_argument_count, flags);
1034 Node* stub_code = jsgraph()->HeapConstant(callable.code());
1035
1036 // We pass the spread in a register, not on the stack.
1037 Node* stub_arity =
1038 jsgraph()->Int32Constant(JSParameterCount(arg_count - kTheSpread));
1039
1040 // Shuffling inputs.
1041 // Before: {target, receiver, ...args, spread, vector}.
1042
1043 node->RemoveInput(n.FeedbackVectorIndex());
1044 Node* spread = node->RemoveInput(n.LastArgumentIndex());
1045
1046 node->InsertInput(zone(), 0, stub_code);
1047 node->InsertInput(zone(), 2, stub_arity);
1048 node->InsertInput(zone(), 3, spread);
1049
1050 // After: {code, target, arity, spread, receiver, ...args}.
1051
1052 NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
1053 }
1054
LowerJSCallRuntime(Node * node)1055 void JSGenericLowering::LowerJSCallRuntime(Node* node) {
1056 const CallRuntimeParameters& p = CallRuntimeParametersOf(node->op());
1057 ReplaceWithRuntimeCall(node, p.id(), static_cast<int>(p.arity()));
1058 }
1059
1060 #if V8_ENABLE_WEBASSEMBLY
1061 // Will be lowered in SimplifiedLowering.
LowerJSWasmCall(Node * node)1062 void JSGenericLowering::LowerJSWasmCall(Node* node) {}
1063 #endif // V8_ENABLE_WEBASSEMBLY
1064
LowerJSForInPrepare(Node * node)1065 void JSGenericLowering::LowerJSForInPrepare(Node* node) {
1066 JSForInPrepareNode n(node);
1067 Effect effect(node); // {node} is kept in the effect chain.
1068 Control control = n.control(); // .. but not in the control chain.
1069 Node* enumerator = n.enumerator();
1070 Node* slot =
1071 jsgraph()->UintPtrConstant(n.Parameters().feedback().slot.ToInt());
1072
1073 std::vector<Edge> use_edges;
1074 for (Edge edge : node->use_edges()) use_edges.push_back(edge);
1075
1076 // {node} will be changed to a builtin call (see below). The returned value
1077 // is a fixed array containing {cache_array} and {cache_length}.
1078 // TODO(jgruber): This is awkward; what we really want is two return values,
1079 // the {cache_array} and {cache_length}, or better yet three return values
1080 // s.t. we can avoid the graph rewrites below. Builtin support for multiple
1081 // return types is unclear though.
1082
1083 Node* result_fixed_array = node;
1084 Node* cache_type = enumerator; // Just to clarify the rename.
1085 Node* cache_array;
1086 Node* cache_length;
1087
1088 cache_array = effect = graph()->NewNode(
1089 machine()->Load(MachineType::AnyTagged()), result_fixed_array,
1090 jsgraph()->IntPtrConstant(FixedArray::OffsetOfElementAt(0) -
1091 kHeapObjectTag),
1092 effect, control);
1093 cache_length = effect = graph()->NewNode(
1094 machine()->Load(MachineType::AnyTagged()), result_fixed_array,
1095 jsgraph()->IntPtrConstant(FixedArray::OffsetOfElementAt(1) -
1096 kHeapObjectTag),
1097 effect, control);
1098
1099 // Update the uses of {node}.
1100 for (Edge edge : use_edges) {
1101 Node* const user = edge.from();
1102 if (NodeProperties::IsEffectEdge(edge)) {
1103 edge.UpdateTo(effect);
1104 } else if (NodeProperties::IsControlEdge(edge)) {
1105 edge.UpdateTo(control);
1106 } else {
1107 DCHECK(NodeProperties::IsValueEdge(edge));
1108 switch (ProjectionIndexOf(user->op())) {
1109 case 0:
1110 Replace(user, cache_type);
1111 break;
1112 case 1:
1113 Replace(user, cache_array);
1114 break;
1115 case 2:
1116 Replace(user, cache_length);
1117 break;
1118 default:
1119 UNREACHABLE();
1120 }
1121 }
1122 }
1123
1124 // Finally, change the original node into a builtin call. This happens here,
1125 // after graph rewrites, since the Call does not have a control output and
1126 // thus must not have any control uses. Any previously existing control
1127 // outputs have been replaced by the graph rewrite above.
1128 node->InsertInput(zone(), n.FeedbackVectorIndex(), slot);
1129 ReplaceWithBuiltinCall(node, Builtin::kForInPrepare);
1130 }
1131
LowerJSForInNext(Node * node)1132 void JSGenericLowering::LowerJSForInNext(Node* node) {
1133 JSForInNextNode n(node);
1134 node->InsertInput(
1135 zone(), 0,
1136 jsgraph()->UintPtrConstant(n.Parameters().feedback().slot.ToInt()));
1137 ReplaceWithBuiltinCall(node, Builtin::kForInNext);
1138 }
1139
LowerJSLoadMessage(Node * node)1140 void JSGenericLowering::LowerJSLoadMessage(Node* node) {
1141 UNREACHABLE(); // Eliminated in typed lowering.
1142 }
1143
1144
LowerJSStoreMessage(Node * node)1145 void JSGenericLowering::LowerJSStoreMessage(Node* node) {
1146 UNREACHABLE(); // Eliminated in typed lowering.
1147 }
1148
LowerJSLoadModule(Node * node)1149 void JSGenericLowering::LowerJSLoadModule(Node* node) {
1150 UNREACHABLE(); // Eliminated in typed lowering.
1151 }
1152
LowerJSStoreModule(Node * node)1153 void JSGenericLowering::LowerJSStoreModule(Node* node) {
1154 UNREACHABLE(); // Eliminated in typed lowering.
1155 }
1156
LowerJSGetImportMeta(Node * node)1157 void JSGenericLowering::LowerJSGetImportMeta(Node* node) {
1158 ReplaceWithRuntimeCall(node, Runtime::kGetImportMetaObject);
1159 }
1160
LowerJSGeneratorStore(Node * node)1161 void JSGenericLowering::LowerJSGeneratorStore(Node* node) {
1162 UNREACHABLE(); // Eliminated in typed lowering.
1163 }
1164
LowerJSGeneratorRestoreContinuation(Node * node)1165 void JSGenericLowering::LowerJSGeneratorRestoreContinuation(Node* node) {
1166 UNREACHABLE(); // Eliminated in typed lowering.
1167 }
1168
LowerJSGeneratorRestoreContext(Node * node)1169 void JSGenericLowering::LowerJSGeneratorRestoreContext(Node* node) {
1170 UNREACHABLE(); // Eliminated in typed lowering.
1171 }
1172
LowerJSGeneratorRestoreInputOrDebugPos(Node * node)1173 void JSGenericLowering::LowerJSGeneratorRestoreInputOrDebugPos(Node* node) {
1174 UNREACHABLE(); // Eliminated in typed lowering.
1175 }
1176
LowerJSGeneratorRestoreRegister(Node * node)1177 void JSGenericLowering::LowerJSGeneratorRestoreRegister(Node* node) {
1178 UNREACHABLE(); // Eliminated in typed lowering.
1179 }
1180
1181 namespace {
1182
StackCheckKindOfJSStackCheck(const Operator * op)1183 StackCheckKind StackCheckKindOfJSStackCheck(const Operator* op) {
1184 DCHECK(op->opcode() == IrOpcode::kJSStackCheck);
1185 return OpParameter<StackCheckKind>(op);
1186 }
1187
1188 } // namespace
1189
LowerJSStackCheck(Node * node)1190 void JSGenericLowering::LowerJSStackCheck(Node* node) {
1191 Node* effect = NodeProperties::GetEffectInput(node);
1192 Node* control = NodeProperties::GetControlInput(node);
1193
1194 Node* limit = effect =
1195 graph()->NewNode(machine()->Load(MachineType::Pointer()),
1196 jsgraph()->ExternalConstant(
1197 ExternalReference::address_of_jslimit(isolate())),
1198 jsgraph()->IntPtrConstant(0), effect, control);
1199
1200 StackCheckKind stack_check_kind = StackCheckKindOfJSStackCheck(node->op());
1201 Node* check = effect = graph()->NewNode(
1202 machine()->StackPointerGreaterThan(stack_check_kind), limit, effect);
1203 Node* branch =
1204 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
1205
1206 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
1207 Node* etrue = effect;
1208
1209 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
1210 NodeProperties::ReplaceControlInput(node, if_false);
1211 NodeProperties::ReplaceEffectInput(node, effect);
1212 Node* efalse = if_false = node;
1213
1214 Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
1215 Node* ephi = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, merge);
1216
1217 // Wire the new diamond into the graph, {node} can still throw.
1218 NodeProperties::ReplaceUses(node, node, ephi, merge, merge);
1219 NodeProperties::ReplaceControlInput(merge, if_false, 1);
1220 NodeProperties::ReplaceEffectInput(ephi, efalse, 1);
1221
1222 // This iteration cuts out potential {IfSuccess} or {IfException} projection
1223 // uses of the original node and places them inside the diamond, so that we
1224 // can change the original {node} into the slow-path runtime call.
1225 for (Edge edge : merge->use_edges()) {
1226 if (!NodeProperties::IsControlEdge(edge)) continue;
1227 if (edge.from()->opcode() == IrOpcode::kIfSuccess) {
1228 NodeProperties::ReplaceUses(edge.from(), nullptr, nullptr, merge);
1229 NodeProperties::ReplaceControlInput(merge, edge.from(), 1);
1230 edge.UpdateTo(node);
1231 }
1232 if (edge.from()->opcode() == IrOpcode::kIfException) {
1233 NodeProperties::ReplaceEffectInput(edge.from(), node);
1234 edge.UpdateTo(node);
1235 }
1236 }
1237
1238 // Turn the stack check into a runtime call. At function entry, the runtime
1239 // function takes an offset argument which is subtracted from the stack
1240 // pointer prior to the stack check (i.e. the check is `sp - offset >=
1241 // limit`).
1242 if (stack_check_kind == StackCheckKind::kJSFunctionEntry) {
1243 node->InsertInput(zone(), 0,
1244 graph()->NewNode(machine()->LoadStackCheckOffset()));
1245 ReplaceWithRuntimeCall(node, Runtime::kStackGuardWithGap);
1246 } else {
1247 ReplaceWithRuntimeCall(node, Runtime::kStackGuard);
1248 }
1249 }
1250
LowerJSDebugger(Node * node)1251 void JSGenericLowering::LowerJSDebugger(Node* node) {
1252 ReplaceWithRuntimeCall(node, Runtime::kHandleDebuggerStatement);
1253 }
1254
zone() const1255 Zone* JSGenericLowering::zone() const { return graph()->zone(); }
1256
1257
isolate() const1258 Isolate* JSGenericLowering::isolate() const { return jsgraph()->isolate(); }
1259
1260
graph() const1261 Graph* JSGenericLowering::graph() const { return jsgraph()->graph(); }
1262
1263
common() const1264 CommonOperatorBuilder* JSGenericLowering::common() const {
1265 return jsgraph()->common();
1266 }
1267
1268
machine() const1269 MachineOperatorBuilder* JSGenericLowering::machine() const {
1270 return jsgraph()->machine();
1271 }
1272
1273 } // namespace compiler
1274 } // namespace internal
1275 } // namespace v8
1276