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-typed-lowering.h"
6
7 #include "src/ast/modules.h"
8 #include "src/builtins/builtins-utils.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/allocation-builder.h"
13 #include "src/compiler/compilation-dependencies.h"
14 #include "src/compiler/graph-assembler.h"
15 #include "src/compiler/js-graph.h"
16 #include "src/compiler/js-heap-broker.h"
17 #include "src/compiler/linkage.h"
18 #include "src/compiler/node-matchers.h"
19 #include "src/compiler/node-properties.h"
20 #include "src/compiler/operator-properties.h"
21 #include "src/compiler/type-cache.h"
22 #include "src/compiler/types.h"
23 #include "src/execution/protectors.h"
24 #include "src/objects/js-generator.h"
25 #include "src/objects/module-inl.h"
26 #include "src/objects/objects-inl.h"
27
28 namespace v8 {
29 namespace internal {
30 namespace compiler {
31
32 // A helper class to simplify the process of reducing a single binop node with a
33 // JSOperator. This class manages the rewriting of context, control, and effect
34 // dependencies during lowering of a binop and contains numerous helper
35 // functions for matching the types of inputs to an operation.
36 class JSBinopReduction final {
37 public:
JSBinopReduction(JSTypedLowering * lowering,Node * node)38 JSBinopReduction(JSTypedLowering* lowering, Node* node)
39 : lowering_(lowering), node_(node) {}
40
GetCompareNumberOperationHint(NumberOperationHint * hint)41 bool GetCompareNumberOperationHint(NumberOperationHint* hint) {
42 DCHECK_EQ(1, node_->op()->EffectOutputCount());
43 switch (GetCompareOperationHint(node_)) {
44 case CompareOperationHint::kSignedSmall:
45 *hint = NumberOperationHint::kSignedSmall;
46 return true;
47 case CompareOperationHint::kNumber:
48 *hint = NumberOperationHint::kNumber;
49 return true;
50 case CompareOperationHint::kNumberOrBoolean:
51 *hint = NumberOperationHint::kNumberOrBoolean;
52 return true;
53 case CompareOperationHint::kNumberOrOddball:
54 *hint = NumberOperationHint::kNumberOrOddball;
55 return true;
56 case CompareOperationHint::kAny:
57 case CompareOperationHint::kNone:
58 case CompareOperationHint::kString:
59 case CompareOperationHint::kSymbol:
60 case CompareOperationHint::kBigInt:
61 case CompareOperationHint::kReceiver:
62 case CompareOperationHint::kReceiverOrNullOrUndefined:
63 case CompareOperationHint::kInternalizedString:
64 break;
65 }
66 return false;
67 }
68
IsInternalizedStringCompareOperation()69 bool IsInternalizedStringCompareOperation() {
70 DCHECK_EQ(1, node_->op()->EffectOutputCount());
71 return (GetCompareOperationHint(node_) ==
72 CompareOperationHint::kInternalizedString) &&
73 BothInputsMaybe(Type::InternalizedString());
74 }
75
IsReceiverCompareOperation()76 bool IsReceiverCompareOperation() {
77 DCHECK_EQ(1, node_->op()->EffectOutputCount());
78 return (GetCompareOperationHint(node_) ==
79 CompareOperationHint::kReceiver) &&
80 BothInputsMaybe(Type::Receiver());
81 }
82
IsReceiverOrNullOrUndefinedCompareOperation()83 bool IsReceiverOrNullOrUndefinedCompareOperation() {
84 DCHECK_EQ(1, node_->op()->EffectOutputCount());
85 return (GetCompareOperationHint(node_) ==
86 CompareOperationHint::kReceiverOrNullOrUndefined) &&
87 BothInputsMaybe(Type::ReceiverOrNullOrUndefined());
88 }
89
IsStringCompareOperation()90 bool IsStringCompareOperation() {
91 DCHECK_EQ(1, node_->op()->EffectOutputCount());
92 return (GetCompareOperationHint(node_) == CompareOperationHint::kString) &&
93 BothInputsMaybe(Type::String());
94 }
95
IsSymbolCompareOperation()96 bool IsSymbolCompareOperation() {
97 DCHECK_EQ(1, node_->op()->EffectOutputCount());
98 return (GetCompareOperationHint(node_) == CompareOperationHint::kSymbol) &&
99 BothInputsMaybe(Type::Symbol());
100 }
101
102 // Check if a string addition will definitely result in creating a ConsString,
103 // i.e. if the combined length of the resulting string exceeds the ConsString
104 // minimum length.
ShouldCreateConsString()105 bool ShouldCreateConsString() {
106 DCHECK_EQ(IrOpcode::kJSAdd, node_->opcode());
107 DCHECK(OneInputIs(Type::String()));
108 if (BothInputsAre(Type::String()) ||
109 GetBinaryOperationHint(node_) == BinaryOperationHint::kString) {
110 HeapObjectBinopMatcher m(node_);
111 JSHeapBroker* broker = lowering_->broker();
112 if (m.right().HasResolvedValue() && m.right().Ref(broker).IsString()) {
113 StringRef right_string = m.right().Ref(broker).AsString();
114 if (right_string.length().has_value() &&
115 right_string.length().value() >= ConsString::kMinLength)
116 return true;
117 }
118 if (m.left().HasResolvedValue() && m.left().Ref(broker).IsString()) {
119 StringRef left_string = m.left().Ref(broker).AsString();
120 if (left_string.length().has_value() &&
121 left_string.length().value() >= ConsString::kMinLength) {
122 // The invariant for ConsString requires the left hand side to be
123 // a sequential or external string if the right hand side is the
124 // empty string. Since we don't know anything about the right hand
125 // side here, we must ensure that the left hand side satisfy the
126 // constraints independent of the right hand side.
127 return left_string.IsSeqString() || left_string.IsExternalString();
128 }
129 }
130 }
131 return false;
132 }
133
134 // Inserts a CheckReceiver for the left input.
CheckLeftInputToReceiver()135 void CheckLeftInputToReceiver() {
136 Node* left_input = graph()->NewNode(simplified()->CheckReceiver(), left(),
137 effect(), control());
138 node_->ReplaceInput(0, left_input);
139 update_effect(left_input);
140 }
141
142 // Inserts a CheckReceiverOrNullOrUndefined for the left input.
CheckLeftInputToReceiverOrNullOrUndefined()143 void CheckLeftInputToReceiverOrNullOrUndefined() {
144 Node* left_input =
145 graph()->NewNode(simplified()->CheckReceiverOrNullOrUndefined(), left(),
146 effect(), control());
147 node_->ReplaceInput(0, left_input);
148 update_effect(left_input);
149 }
150
151 // Checks that both inputs are Receiver, and if we don't know
152 // statically that one side is already a Receiver, insert a
153 // CheckReceiver node.
CheckInputsToReceiver()154 void CheckInputsToReceiver() {
155 if (!left_type().Is(Type::Receiver())) {
156 CheckLeftInputToReceiver();
157 }
158 if (!right_type().Is(Type::Receiver())) {
159 Node* right_input = graph()->NewNode(simplified()->CheckReceiver(),
160 right(), effect(), control());
161 node_->ReplaceInput(1, right_input);
162 update_effect(right_input);
163 }
164 }
165
166 // Checks that both inputs are Receiver, Null or Undefined and if
167 // we don't know statically that one side is already a Receiver,
168 // Null or Undefined, insert CheckReceiverOrNullOrUndefined nodes.
CheckInputsToReceiverOrNullOrUndefined()169 void CheckInputsToReceiverOrNullOrUndefined() {
170 if (!left_type().Is(Type::ReceiverOrNullOrUndefined())) {
171 CheckLeftInputToReceiverOrNullOrUndefined();
172 }
173 if (!right_type().Is(Type::ReceiverOrNullOrUndefined())) {
174 Node* right_input =
175 graph()->NewNode(simplified()->CheckReceiverOrNullOrUndefined(),
176 right(), effect(), control());
177 node_->ReplaceInput(1, right_input);
178 update_effect(right_input);
179 }
180 }
181
182 // Inserts a CheckSymbol for the left input.
CheckLeftInputToSymbol()183 void CheckLeftInputToSymbol() {
184 Node* left_input = graph()->NewNode(simplified()->CheckSymbol(), left(),
185 effect(), control());
186 node_->ReplaceInput(0, left_input);
187 update_effect(left_input);
188 }
189
190 // Checks that both inputs are Symbol, and if we don't know
191 // statically that one side is already a Symbol, insert a
192 // CheckSymbol node.
CheckInputsToSymbol()193 void CheckInputsToSymbol() {
194 if (!left_type().Is(Type::Symbol())) {
195 CheckLeftInputToSymbol();
196 }
197 if (!right_type().Is(Type::Symbol())) {
198 Node* right_input = graph()->NewNode(simplified()->CheckSymbol(), right(),
199 effect(), control());
200 node_->ReplaceInput(1, right_input);
201 update_effect(right_input);
202 }
203 }
204
205 // Checks that both inputs are String, and if we don't know
206 // statically that one side is already a String, insert a
207 // CheckString node.
CheckInputsToString()208 void CheckInputsToString() {
209 if (!left_type().Is(Type::String())) {
210 Node* left_input =
211 graph()->NewNode(simplified()->CheckString(FeedbackSource()), left(),
212 effect(), control());
213 node_->ReplaceInput(0, left_input);
214 update_effect(left_input);
215 }
216 if (!right_type().Is(Type::String())) {
217 Node* right_input =
218 graph()->NewNode(simplified()->CheckString(FeedbackSource()), right(),
219 effect(), control());
220 node_->ReplaceInput(1, right_input);
221 update_effect(right_input);
222 }
223 }
224
225 // Checks that both inputs are InternalizedString, and if we don't know
226 // statically that one side is already an InternalizedString, insert a
227 // CheckInternalizedString node.
CheckInputsToInternalizedString()228 void CheckInputsToInternalizedString() {
229 if (!left_type().Is(Type::UniqueName())) {
230 Node* left_input = graph()->NewNode(
231 simplified()->CheckInternalizedString(), left(), effect(), control());
232 node_->ReplaceInput(0, left_input);
233 update_effect(left_input);
234 }
235 if (!right_type().Is(Type::UniqueName())) {
236 Node* right_input =
237 graph()->NewNode(simplified()->CheckInternalizedString(), right(),
238 effect(), control());
239 node_->ReplaceInput(1, right_input);
240 update_effect(right_input);
241 }
242 }
243
ConvertInputsToNumber()244 void ConvertInputsToNumber() {
245 DCHECK(left_type().Is(Type::PlainPrimitive()));
246 DCHECK(right_type().Is(Type::PlainPrimitive()));
247 node_->ReplaceInput(0, ConvertPlainPrimitiveToNumber(left()));
248 node_->ReplaceInput(1, ConvertPlainPrimitiveToNumber(right()));
249 }
250
ConvertInputsToUI32(Signedness left_signedness,Signedness right_signedness)251 void ConvertInputsToUI32(Signedness left_signedness,
252 Signedness right_signedness) {
253 node_->ReplaceInput(0, ConvertToUI32(left(), left_signedness));
254 node_->ReplaceInput(1, ConvertToUI32(right(), right_signedness));
255 }
256
SwapInputs()257 void SwapInputs() {
258 Node* l = left();
259 Node* r = right();
260 node_->ReplaceInput(0, r);
261 node_->ReplaceInput(1, l);
262 }
263
264 // Remove all effect and control inputs and outputs to this node and change
265 // to the pure operator {op}.
ChangeToPureOperator(const Operator * op,Type type=Type::Any ())266 Reduction ChangeToPureOperator(const Operator* op, Type type = Type::Any()) {
267 DCHECK_EQ(0, op->EffectInputCount());
268 DCHECK_EQ(false, OperatorProperties::HasContextInput(op));
269 DCHECK_EQ(0, op->ControlInputCount());
270 DCHECK_EQ(2, op->ValueInputCount());
271
272 // Remove the effects from the node, and update its effect/control usages.
273 if (node_->op()->EffectInputCount() > 0) {
274 lowering_->RelaxEffectsAndControls(node_);
275 }
276 // Remove the inputs corresponding to context, effect, and control.
277 NodeProperties::RemoveNonValueInputs(node_);
278 // Remove the feedback vector input, if applicable.
279 if (JSOperator::IsBinaryWithFeedback(node_->opcode())) {
280 node_->RemoveInput(JSBinaryOpNode::FeedbackVectorIndex());
281 }
282 // Finally, update the operator to the new one.
283 NodeProperties::ChangeOp(node_, op);
284
285 // TODO(jarin): Replace the explicit typing hack with a call to some method
286 // that encapsulates changing the operator and re-typing.
287 Type node_type = NodeProperties::GetType(node_);
288 NodeProperties::SetType(node_, Type::Intersect(node_type, type, zone()));
289
290 return lowering_->Changed(node_);
291 }
292
ChangeToSpeculativeOperator(const Operator * op,Type upper_bound)293 Reduction ChangeToSpeculativeOperator(const Operator* op, Type upper_bound) {
294 DCHECK_EQ(1, op->EffectInputCount());
295 DCHECK_EQ(1, op->EffectOutputCount());
296 DCHECK_EQ(false, OperatorProperties::HasContextInput(op));
297 DCHECK_EQ(1, op->ControlInputCount());
298 DCHECK_EQ(0, op->ControlOutputCount());
299 DCHECK_EQ(0, OperatorProperties::GetFrameStateInputCount(op));
300 DCHECK_EQ(2, op->ValueInputCount());
301
302 DCHECK_EQ(1, node_->op()->EffectInputCount());
303 DCHECK_EQ(1, node_->op()->EffectOutputCount());
304 DCHECK_EQ(1, node_->op()->ControlInputCount());
305
306 // Reconnect the control output to bypass the IfSuccess node and
307 // possibly disconnect from the IfException node.
308 lowering_->RelaxControls(node_);
309
310 // Remove the frame state and the context.
311 if (OperatorProperties::HasFrameStateInput(node_->op())) {
312 node_->RemoveInput(NodeProperties::FirstFrameStateIndex(node_));
313 }
314 node_->RemoveInput(NodeProperties::FirstContextIndex(node_));
315
316 // Remove the feedback vector input, if applicable.
317 if (JSOperator::IsBinaryWithFeedback(node_->opcode())) {
318 node_->RemoveInput(JSBinaryOpNode::FeedbackVectorIndex());
319 }
320 // Finally, update the operator to the new one.
321 NodeProperties::ChangeOp(node_, op);
322
323 // Update the type to number.
324 Type node_type = NodeProperties::GetType(node_);
325 NodeProperties::SetType(node_,
326 Type::Intersect(node_type, upper_bound, zone()));
327
328 return lowering_->Changed(node_);
329 }
330
NumberOp()331 const Operator* NumberOp() {
332 switch (node_->opcode()) {
333 case IrOpcode::kJSAdd:
334 return simplified()->NumberAdd();
335 case IrOpcode::kJSSubtract:
336 return simplified()->NumberSubtract();
337 case IrOpcode::kJSMultiply:
338 return simplified()->NumberMultiply();
339 case IrOpcode::kJSDivide:
340 return simplified()->NumberDivide();
341 case IrOpcode::kJSModulus:
342 return simplified()->NumberModulus();
343 case IrOpcode::kJSExponentiate:
344 return simplified()->NumberPow();
345 case IrOpcode::kJSBitwiseAnd:
346 return simplified()->NumberBitwiseAnd();
347 case IrOpcode::kJSBitwiseOr:
348 return simplified()->NumberBitwiseOr();
349 case IrOpcode::kJSBitwiseXor:
350 return simplified()->NumberBitwiseXor();
351 case IrOpcode::kJSShiftLeft:
352 return simplified()->NumberShiftLeft();
353 case IrOpcode::kJSShiftRight:
354 return simplified()->NumberShiftRight();
355 case IrOpcode::kJSShiftRightLogical:
356 return simplified()->NumberShiftRightLogical();
357 default:
358 break;
359 }
360 UNREACHABLE();
361 }
362
LeftInputIs(Type t)363 bool LeftInputIs(Type t) { return left_type().Is(t); }
364
RightInputIs(Type t)365 bool RightInputIs(Type t) { return right_type().Is(t); }
366
OneInputIs(Type t)367 bool OneInputIs(Type t) { return LeftInputIs(t) || RightInputIs(t); }
368
BothInputsAre(Type t)369 bool BothInputsAre(Type t) { return LeftInputIs(t) && RightInputIs(t); }
370
BothInputsMaybe(Type t)371 bool BothInputsMaybe(Type t) {
372 return left_type().Maybe(t) && right_type().Maybe(t);
373 }
374
OneInputCannotBe(Type t)375 bool OneInputCannotBe(Type t) {
376 return !left_type().Maybe(t) || !right_type().Maybe(t);
377 }
378
NeitherInputCanBe(Type t)379 bool NeitherInputCanBe(Type t) {
380 return !left_type().Maybe(t) && !right_type().Maybe(t);
381 }
382
GetBinaryOperationHint(Node * node) const383 BinaryOperationHint GetBinaryOperationHint(Node* node) const {
384 const FeedbackParameter& p = FeedbackParameterOf(node->op());
385 return lowering_->broker()->GetFeedbackForBinaryOperation(p.feedback());
386 }
387
effect()388 Node* effect() { return NodeProperties::GetEffectInput(node_); }
control()389 Node* control() { return NodeProperties::GetControlInput(node_); }
context()390 Node* context() { return NodeProperties::GetContextInput(node_); }
left()391 Node* left() { return NodeProperties::GetValueInput(node_, 0); }
right()392 Node* right() { return NodeProperties::GetValueInput(node_, 1); }
left_type()393 Type left_type() { return NodeProperties::GetType(node_->InputAt(0)); }
right_type()394 Type right_type() { return NodeProperties::GetType(node_->InputAt(1)); }
type()395 Type type() { return NodeProperties::GetType(node_); }
396
simplified()397 SimplifiedOperatorBuilder* simplified() { return lowering_->simplified(); }
graph() const398 Graph* graph() const { return lowering_->graph(); }
jsgraph()399 JSGraph* jsgraph() { return lowering_->jsgraph(); }
isolate()400 Isolate* isolate() { return jsgraph()->isolate(); }
javascript()401 JSOperatorBuilder* javascript() { return lowering_->javascript(); }
common()402 CommonOperatorBuilder* common() { return jsgraph()->common(); }
zone() const403 Zone* zone() const { return graph()->zone(); }
404
405 private:
406 JSTypedLowering* lowering_; // The containing lowering instance.
407 Node* node_; // The original node.
408
ConvertPlainPrimitiveToNumber(Node * node)409 Node* ConvertPlainPrimitiveToNumber(Node* node) {
410 DCHECK(NodeProperties::GetType(node).Is(Type::PlainPrimitive()));
411 // Avoid inserting too many eager ToNumber() operations.
412 Reduction const reduction = lowering_->ReduceJSToNumberInput(node);
413 if (reduction.Changed()) return reduction.replacement();
414 if (NodeProperties::GetType(node).Is(Type::Number())) {
415 return node;
416 }
417 return graph()->NewNode(simplified()->PlainPrimitiveToNumber(), node);
418 }
419
ConvertToUI32(Node * node,Signedness signedness)420 Node* ConvertToUI32(Node* node, Signedness signedness) {
421 // Avoid introducing too many eager NumberToXXnt32() operations.
422 Type type = NodeProperties::GetType(node);
423 if (signedness == kSigned) {
424 if (!type.Is(Type::Signed32())) {
425 node = graph()->NewNode(simplified()->NumberToInt32(), node);
426 }
427 } else {
428 DCHECK_EQ(kUnsigned, signedness);
429 if (!type.Is(Type::Unsigned32())) {
430 node = graph()->NewNode(simplified()->NumberToUint32(), node);
431 }
432 }
433 return node;
434 }
435
GetCompareOperationHint(Node * node) const436 CompareOperationHint GetCompareOperationHint(Node* node) const {
437 const FeedbackParameter& p = FeedbackParameterOf(node->op());
438 return lowering_->broker()->GetFeedbackForCompareOperation(p.feedback());
439 }
440
update_effect(Node * effect)441 void update_effect(Node* effect) {
442 NodeProperties::ReplaceEffectInput(node_, effect);
443 }
444 };
445
446
447 // TODO(turbofan): js-typed-lowering improvements possible
448 // - immediately put in type bounds for all new nodes
449 // - relax effects from generic but not-side-effecting operations
450
JSTypedLowering(Editor * editor,JSGraph * jsgraph,JSHeapBroker * broker,Zone * zone)451 JSTypedLowering::JSTypedLowering(Editor* editor, JSGraph* jsgraph,
452 JSHeapBroker* broker, Zone* zone)
453 : AdvancedReducer(editor),
454 jsgraph_(jsgraph),
455 broker_(broker),
456 empty_string_type_(
457 Type::Constant(broker, factory()->empty_string(), graph()->zone())),
458 pointer_comparable_type_(
459 Type::Union(Type::Oddball(),
460 Type::Union(Type::SymbolOrReceiver(), empty_string_type_,
461 graph()->zone()),
462 graph()->zone())),
463 type_cache_(TypeCache::Get()) {}
464
ReduceJSBitwiseNot(Node * node)465 Reduction JSTypedLowering::ReduceJSBitwiseNot(Node* node) {
466 Node* input = NodeProperties::GetValueInput(node, 0);
467 Type input_type = NodeProperties::GetType(input);
468 if (input_type.Is(Type::PlainPrimitive())) {
469 // JSBitwiseNot(x) => NumberBitwiseXor(ToInt32(x), -1)
470 const FeedbackParameter& p = FeedbackParameterOf(node->op());
471 node->InsertInput(graph()->zone(), 1, jsgraph()->SmiConstant(-1));
472 NodeProperties::ChangeOp(node, javascript()->BitwiseXor(p.feedback()));
473 JSBinopReduction r(this, node);
474 r.ConvertInputsToNumber();
475 r.ConvertInputsToUI32(kSigned, kSigned);
476 return r.ChangeToPureOperator(r.NumberOp(), Type::Signed32());
477 }
478 return NoChange();
479 }
480
ReduceJSDecrement(Node * node)481 Reduction JSTypedLowering::ReduceJSDecrement(Node* node) {
482 Node* input = NodeProperties::GetValueInput(node, 0);
483 Type input_type = NodeProperties::GetType(input);
484 if (input_type.Is(Type::PlainPrimitive())) {
485 // JSDecrement(x) => NumberSubtract(ToNumber(x), 1)
486 const FeedbackParameter& p = FeedbackParameterOf(node->op());
487 node->InsertInput(graph()->zone(), 1, jsgraph()->OneConstant());
488 NodeProperties::ChangeOp(node, javascript()->Subtract(p.feedback()));
489 JSBinopReduction r(this, node);
490 r.ConvertInputsToNumber();
491 DCHECK_EQ(simplified()->NumberSubtract(), r.NumberOp());
492 return r.ChangeToPureOperator(r.NumberOp(), Type::Number());
493 }
494 return NoChange();
495 }
496
ReduceJSIncrement(Node * node)497 Reduction JSTypedLowering::ReduceJSIncrement(Node* node) {
498 Node* input = NodeProperties::GetValueInput(node, 0);
499 Type input_type = NodeProperties::GetType(input);
500 if (input_type.Is(Type::PlainPrimitive())) {
501 // JSIncrement(x) => NumberAdd(ToNumber(x), 1)
502 const FeedbackParameter& p = FeedbackParameterOf(node->op());
503 node->InsertInput(graph()->zone(), 1, jsgraph()->OneConstant());
504 NodeProperties::ChangeOp(node, javascript()->Add(p.feedback()));
505 JSBinopReduction r(this, node);
506 r.ConvertInputsToNumber();
507 DCHECK_EQ(simplified()->NumberAdd(), r.NumberOp());
508 return r.ChangeToPureOperator(r.NumberOp(), Type::Number());
509 }
510 return NoChange();
511 }
512
ReduceJSNegate(Node * node)513 Reduction JSTypedLowering::ReduceJSNegate(Node* node) {
514 Node* input = NodeProperties::GetValueInput(node, 0);
515 Type input_type = NodeProperties::GetType(input);
516 if (input_type.Is(Type::PlainPrimitive())) {
517 // JSNegate(x) => NumberMultiply(ToNumber(x), -1)
518 const FeedbackParameter& p = FeedbackParameterOf(node->op());
519 node->InsertInput(graph()->zone(), 1, jsgraph()->SmiConstant(-1));
520 NodeProperties::ChangeOp(node, javascript()->Multiply(p.feedback()));
521 JSBinopReduction r(this, node);
522 r.ConvertInputsToNumber();
523 return r.ChangeToPureOperator(r.NumberOp(), Type::Number());
524 }
525 return NoChange();
526 }
527
ReduceJSAdd(Node * node)528 Reduction JSTypedLowering::ReduceJSAdd(Node* node) {
529 JSBinopReduction r(this, node);
530 if (r.BothInputsAre(Type::Number())) {
531 // JSAdd(x:number, y:number) => NumberAdd(x, y)
532 return r.ChangeToPureOperator(simplified()->NumberAdd(), Type::Number());
533 }
534 if (r.BothInputsAre(Type::PlainPrimitive()) &&
535 r.NeitherInputCanBe(Type::StringOrReceiver())) {
536 // JSAdd(x:-string, y:-string) => NumberAdd(ToNumber(x), ToNumber(y))
537 r.ConvertInputsToNumber();
538 return r.ChangeToPureOperator(simplified()->NumberAdd(), Type::Number());
539 }
540
541 // Strength-reduce if one input is already known to be a string.
542 if (r.LeftInputIs(Type::String())) {
543 // JSAdd(x:string, y) => JSAdd(x, JSToString(y))
544 Reduction const reduction = ReduceJSToStringInput(r.right());
545 if (reduction.Changed()) {
546 NodeProperties::ReplaceValueInput(node, reduction.replacement(), 1);
547 }
548 } else if (r.RightInputIs(Type::String())) {
549 // JSAdd(x, y:string) => JSAdd(JSToString(x), y)
550 Reduction const reduction = ReduceJSToStringInput(r.left());
551 if (reduction.Changed()) {
552 NodeProperties::ReplaceValueInput(node, reduction.replacement(), 0);
553 }
554 }
555
556 // Always bake in String feedback into the graph.
557 if (r.GetBinaryOperationHint(node) == BinaryOperationHint::kString) {
558 r.CheckInputsToString();
559 }
560
561 // Strength-reduce concatenation of empty strings if both sides are
562 // primitives, as in that case the ToPrimitive on the other side is
563 // definitely going to be a no-op.
564 if (r.BothInputsAre(Type::Primitive())) {
565 if (r.LeftInputIs(empty_string_type_)) {
566 // JSAdd("", x:primitive) => JSToString(x)
567 NodeProperties::ReplaceValueInputs(node, r.right());
568 NodeProperties::ChangeOp(node, javascript()->ToString());
569 NodeProperties::SetType(
570 node, Type::Intersect(r.type(), Type::String(), graph()->zone()));
571 return Changed(node).FollowedBy(ReduceJSToString(node));
572 } else if (r.RightInputIs(empty_string_type_)) {
573 // JSAdd(x:primitive, "") => JSToString(x)
574 NodeProperties::ReplaceValueInputs(node, r.left());
575 NodeProperties::ChangeOp(node, javascript()->ToString());
576 NodeProperties::SetType(
577 node, Type::Intersect(r.type(), Type::String(), graph()->zone()));
578 return Changed(node).FollowedBy(ReduceJSToString(node));
579 }
580 }
581
582 // Lower to string addition if both inputs are known to be strings.
583 if (r.BothInputsAre(Type::String())) {
584 Node* context = NodeProperties::GetContextInput(node);
585 Node* frame_state = NodeProperties::GetFrameStateInput(node);
586 Node* effect = NodeProperties::GetEffectInput(node);
587 Node* control = NodeProperties::GetControlInput(node);
588
589 // Compute the resulting length.
590 Node* left_length =
591 graph()->NewNode(simplified()->StringLength(), r.left());
592 Node* right_length =
593 graph()->NewNode(simplified()->StringLength(), r.right());
594 Node* length =
595 graph()->NewNode(simplified()->NumberAdd(), left_length, right_length);
596
597 PropertyCellRef string_length_protector =
598 MakeRef(broker(), factory()->string_length_protector());
599 string_length_protector.CacheAsProtector();
600
601 if (string_length_protector.value().AsSmi() ==
602 Protectors::kProtectorValid) {
603 // We can just deoptimize if the {length} is out-of-bounds. Besides
604 // generating a shorter code sequence than the version below, this
605 // has the additional benefit of not holding on to the lazy {frame_state}
606 // and thus potentially reduces the number of live ranges and allows for
607 // more truncations.
608 length = effect = graph()->NewNode(
609 simplified()->CheckBounds(FeedbackSource()), length,
610 jsgraph()->Constant(String::kMaxLength + 1), effect, control);
611 } else {
612 // Check if we would overflow the allowed maximum string length.
613 Node* check =
614 graph()->NewNode(simplified()->NumberLessThanOrEqual(), length,
615 jsgraph()->Constant(String::kMaxLength));
616 Node* branch =
617 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
618 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
619 Node* efalse = effect;
620 {
621 // Throw a RangeError in case of overflow.
622 Node* vfalse = efalse = if_false = graph()->NewNode(
623 javascript()->CallRuntime(Runtime::kThrowInvalidStringLength),
624 context, frame_state, efalse, if_false);
625
626 // Update potential {IfException} uses of {node} to point to the
627 // %ThrowInvalidStringLength runtime call node instead.
628 Node* on_exception = nullptr;
629 if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
630 NodeProperties::ReplaceControlInput(on_exception, vfalse);
631 NodeProperties::ReplaceEffectInput(on_exception, efalse);
632 if_false = graph()->NewNode(common()->IfSuccess(), vfalse);
633 Revisit(on_exception);
634 }
635
636 // The above %ThrowInvalidStringLength runtime call is an unconditional
637 // throw, making it impossible to return a successful completion in this
638 // case. We simply connect the successful completion to the graph end.
639 if_false = graph()->NewNode(common()->Throw(), efalse, if_false);
640 // TODO(bmeurer): This should be on the AdvancedReducer somehow.
641 NodeProperties::MergeControlToEnd(graph(), common(), if_false);
642 Revisit(graph()->end());
643 }
644 control = graph()->NewNode(common()->IfTrue(), branch);
645 length = effect =
646 graph()->NewNode(common()->TypeGuard(type_cache_->kStringLengthType),
647 length, effect, control);
648 }
649
650 // TODO(bmeurer): Ideally this should always use StringConcat and decide to
651 // optimize to NewConsString later during SimplifiedLowering, but for that
652 // to work we need to know that it's safe to create a ConsString.
653 Operator const* const op = r.ShouldCreateConsString()
654 ? simplified()->NewConsString()
655 : simplified()->StringConcat();
656 Node* value = graph()->NewNode(op, length, r.left(), r.right());
657 ReplaceWithValue(node, value, effect, control);
658 return Replace(value);
659 }
660
661 // We never get here when we had String feedback.
662 DCHECK_NE(BinaryOperationHint::kString, r.GetBinaryOperationHint(node));
663 if (r.OneInputIs(Type::String())) {
664 StringAddFlags flags = STRING_ADD_CHECK_NONE;
665 if (!r.LeftInputIs(Type::String())) {
666 flags = STRING_ADD_CONVERT_LEFT;
667 } else if (!r.RightInputIs(Type::String())) {
668 flags = STRING_ADD_CONVERT_RIGHT;
669 }
670 Operator::Properties properties = node->op()->properties();
671 if (r.NeitherInputCanBe(Type::Receiver())) {
672 // Both sides are already strings, so we know that the
673 // string addition will not cause any observable side
674 // effects; it can still throw obviously.
675 properties = Operator::kNoWrite | Operator::kNoDeopt;
676 }
677
678 // JSAdd(x:string, y) => CallStub[StringAdd](x, y)
679 // JSAdd(x, y:string) => CallStub[StringAdd](x, y)
680 Callable const callable = CodeFactory::StringAdd(isolate(), flags);
681 auto call_descriptor = Linkage::GetStubCallDescriptor(
682 graph()->zone(), callable.descriptor(),
683 callable.descriptor().GetStackParameterCount(),
684 CallDescriptor::kNeedsFrameState, properties);
685 DCHECK_EQ(1, OperatorProperties::GetFrameStateInputCount(node->op()));
686 node->RemoveInput(JSAddNode::FeedbackVectorIndex());
687 node->InsertInput(graph()->zone(), 0,
688 jsgraph()->HeapConstant(callable.code()));
689 NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
690 return Changed(node);
691 }
692 return NoChange();
693 }
694
ReduceNumberBinop(Node * node)695 Reduction JSTypedLowering::ReduceNumberBinop(Node* node) {
696 JSBinopReduction r(this, node);
697 if (r.BothInputsAre(Type::PlainPrimitive())) {
698 r.ConvertInputsToNumber();
699 return r.ChangeToPureOperator(r.NumberOp(), Type::Number());
700 }
701 return NoChange();
702 }
703
ReduceInt32Binop(Node * node)704 Reduction JSTypedLowering::ReduceInt32Binop(Node* node) {
705 JSBinopReduction r(this, node);
706 if (r.BothInputsAre(Type::PlainPrimitive())) {
707 r.ConvertInputsToNumber();
708 r.ConvertInputsToUI32(kSigned, kSigned);
709 return r.ChangeToPureOperator(r.NumberOp(), Type::Signed32());
710 }
711 return NoChange();
712 }
713
ReduceUI32Shift(Node * node,Signedness signedness)714 Reduction JSTypedLowering::ReduceUI32Shift(Node* node, Signedness signedness) {
715 JSBinopReduction r(this, node);
716 if (r.BothInputsAre(Type::PlainPrimitive())) {
717 r.ConvertInputsToNumber();
718 r.ConvertInputsToUI32(signedness, kUnsigned);
719 return r.ChangeToPureOperator(r.NumberOp(), signedness == kUnsigned
720 ? Type::Unsigned32()
721 : Type::Signed32());
722 }
723 return NoChange();
724 }
725
ReduceJSComparison(Node * node)726 Reduction JSTypedLowering::ReduceJSComparison(Node* node) {
727 JSBinopReduction r(this, node);
728 if (r.BothInputsAre(Type::String())) {
729 // If both inputs are definitely strings, perform a string comparison.
730 const Operator* stringOp;
731 switch (node->opcode()) {
732 case IrOpcode::kJSLessThan:
733 stringOp = simplified()->StringLessThan();
734 break;
735 case IrOpcode::kJSGreaterThan:
736 stringOp = simplified()->StringLessThan();
737 r.SwapInputs(); // a > b => b < a
738 break;
739 case IrOpcode::kJSLessThanOrEqual:
740 stringOp = simplified()->StringLessThanOrEqual();
741 break;
742 case IrOpcode::kJSGreaterThanOrEqual:
743 stringOp = simplified()->StringLessThanOrEqual();
744 r.SwapInputs(); // a >= b => b <= a
745 break;
746 default:
747 return NoChange();
748 }
749 r.ChangeToPureOperator(stringOp);
750 return Changed(node);
751 }
752
753 const Operator* less_than;
754 const Operator* less_than_or_equal;
755 if (r.BothInputsAre(Type::Signed32()) ||
756 r.BothInputsAre(Type::Unsigned32())) {
757 less_than = simplified()->NumberLessThan();
758 less_than_or_equal = simplified()->NumberLessThanOrEqual();
759 } else if (r.OneInputCannotBe(Type::StringOrReceiver()) &&
760 r.BothInputsAre(Type::PlainPrimitive())) {
761 r.ConvertInputsToNumber();
762 less_than = simplified()->NumberLessThan();
763 less_than_or_equal = simplified()->NumberLessThanOrEqual();
764 } else if (r.IsStringCompareOperation()) {
765 r.CheckInputsToString();
766 less_than = simplified()->StringLessThan();
767 less_than_or_equal = simplified()->StringLessThanOrEqual();
768 } else {
769 return NoChange();
770 }
771 const Operator* comparison;
772 switch (node->opcode()) {
773 case IrOpcode::kJSLessThan:
774 comparison = less_than;
775 break;
776 case IrOpcode::kJSGreaterThan:
777 comparison = less_than;
778 r.SwapInputs(); // a > b => b < a
779 break;
780 case IrOpcode::kJSLessThanOrEqual:
781 comparison = less_than_or_equal;
782 break;
783 case IrOpcode::kJSGreaterThanOrEqual:
784 comparison = less_than_or_equal;
785 r.SwapInputs(); // a >= b => b <= a
786 break;
787 default:
788 return NoChange();
789 }
790 return r.ChangeToPureOperator(comparison);
791 }
792
ReduceJSEqual(Node * node)793 Reduction JSTypedLowering::ReduceJSEqual(Node* node) {
794 JSBinopReduction r(this, node);
795
796 if (r.BothInputsAre(Type::UniqueName())) {
797 return r.ChangeToPureOperator(simplified()->ReferenceEqual());
798 }
799 if (r.IsInternalizedStringCompareOperation()) {
800 r.CheckInputsToInternalizedString();
801 return r.ChangeToPureOperator(simplified()->ReferenceEqual());
802 }
803 if (r.BothInputsAre(Type::String())) {
804 return r.ChangeToPureOperator(simplified()->StringEqual());
805 }
806 if (r.BothInputsAre(Type::Boolean())) {
807 return r.ChangeToPureOperator(simplified()->ReferenceEqual());
808 }
809 if (r.BothInputsAre(Type::Receiver())) {
810 return r.ChangeToPureOperator(simplified()->ReferenceEqual());
811 }
812 if (r.OneInputIs(Type::NullOrUndefined())) {
813 RelaxEffectsAndControls(node);
814 node->RemoveInput(r.LeftInputIs(Type::NullOrUndefined()) ? 0 : 1);
815 node->TrimInputCount(1);
816 NodeProperties::ChangeOp(node, simplified()->ObjectIsUndetectable());
817 return Changed(node);
818 }
819
820 if (r.BothInputsAre(Type::Signed32()) ||
821 r.BothInputsAre(Type::Unsigned32())) {
822 return r.ChangeToPureOperator(simplified()->NumberEqual());
823 } else if (r.BothInputsAre(Type::Number())) {
824 return r.ChangeToPureOperator(simplified()->NumberEqual());
825 } else if (r.IsReceiverCompareOperation()) {
826 r.CheckInputsToReceiver();
827 return r.ChangeToPureOperator(simplified()->ReferenceEqual());
828 } else if (r.IsReceiverOrNullOrUndefinedCompareOperation()) {
829 // Check that both inputs are Receiver, Null or Undefined.
830 r.CheckInputsToReceiverOrNullOrUndefined();
831
832 // If one side is known to be a detectable receiver now, we
833 // can simply perform reference equality here, since this
834 // known detectable receiver is going to only match itself.
835 if (r.OneInputIs(Type::DetectableReceiver())) {
836 return r.ChangeToPureOperator(simplified()->ReferenceEqual());
837 }
838
839 // Known that both sides are Receiver, Null or Undefined, the
840 // abstract equality operation can be performed like this:
841 //
842 // if left == undefined || left == null
843 // then ObjectIsUndetectable(right)
844 // else if right == undefined || right == null
845 // then ObjectIsUndetectable(left)
846 // else ReferenceEqual(left, right)
847 #define __ gasm.
848 JSGraphAssembler gasm(jsgraph(), jsgraph()->zone());
849 gasm.InitializeEffectControl(r.effect(), r.control());
850
851 auto lhs = TNode<Object>::UncheckedCast(r.left());
852 auto rhs = TNode<Object>::UncheckedCast(r.right());
853
854 auto done = __ MakeLabel(MachineRepresentation::kTagged);
855 auto check_undetectable = __ MakeLabel(MachineRepresentation::kTagged);
856
857 __ GotoIf(__ ReferenceEqual(lhs, __ UndefinedConstant()),
858 &check_undetectable, rhs);
859 __ GotoIf(__ ReferenceEqual(lhs, __ NullConstant()), &check_undetectable,
860 rhs);
861 __ GotoIf(__ ReferenceEqual(rhs, __ UndefinedConstant()),
862 &check_undetectable, lhs);
863 __ GotoIf(__ ReferenceEqual(rhs, __ NullConstant()), &check_undetectable,
864 lhs);
865 __ Goto(&done, __ ReferenceEqual(lhs, rhs));
866
867 __ Bind(&check_undetectable);
868 __ Goto(&done,
869 __ ObjectIsUndetectable(check_undetectable.PhiAt<Object>(0)));
870
871 __ Bind(&done);
872 Node* value = done.PhiAt(0);
873 ReplaceWithValue(node, value, gasm.effect(), gasm.control());
874 return Replace(value);
875 #undef __
876 } else if (r.IsStringCompareOperation()) {
877 r.CheckInputsToString();
878 return r.ChangeToPureOperator(simplified()->StringEqual());
879 } else if (r.IsSymbolCompareOperation()) {
880 r.CheckInputsToSymbol();
881 return r.ChangeToPureOperator(simplified()->ReferenceEqual());
882 }
883 return NoChange();
884 }
885
ReduceJSStrictEqual(Node * node)886 Reduction JSTypedLowering::ReduceJSStrictEqual(Node* node) {
887 JSBinopReduction r(this, node);
888 if (r.type().IsSingleton()) {
889 // Let ConstantFoldingReducer handle this.
890 return NoChange();
891 }
892 if (r.left() == r.right()) {
893 // x === x is always true if x != NaN
894 Node* replacement = graph()->NewNode(
895 simplified()->BooleanNot(),
896 graph()->NewNode(simplified()->ObjectIsNaN(), r.left()));
897 DCHECK(NodeProperties::GetType(replacement).Is(r.type()));
898 ReplaceWithValue(node, replacement);
899 return Replace(replacement);
900 }
901
902 if (r.BothInputsAre(Type::Unique())) {
903 return r.ChangeToPureOperator(simplified()->ReferenceEqual());
904 }
905 if (r.OneInputIs(pointer_comparable_type_)) {
906 return r.ChangeToPureOperator(simplified()->ReferenceEqual());
907 }
908 if (r.IsInternalizedStringCompareOperation()) {
909 r.CheckInputsToInternalizedString();
910 return r.ChangeToPureOperator(simplified()->ReferenceEqual());
911 }
912 if (r.BothInputsAre(Type::String())) {
913 return r.ChangeToPureOperator(simplified()->StringEqual());
914 }
915
916 NumberOperationHint hint;
917 if (r.BothInputsAre(Type::Signed32()) ||
918 r.BothInputsAre(Type::Unsigned32())) {
919 return r.ChangeToPureOperator(simplified()->NumberEqual());
920 } else if (r.GetCompareNumberOperationHint(&hint) &&
921 hint != NumberOperationHint::kNumberOrOddball &&
922 hint != NumberOperationHint::kNumberOrBoolean) {
923 // SpeculativeNumberEqual performs implicit conversion of oddballs to
924 // numbers, so me must not generate it for strict equality with respective
925 // hint.
926 DCHECK(hint == NumberOperationHint::kNumber ||
927 hint == NumberOperationHint::kSignedSmall);
928 return r.ChangeToSpeculativeOperator(
929 simplified()->SpeculativeNumberEqual(hint), Type::Boolean());
930 } else if (r.BothInputsAre(Type::Number())) {
931 return r.ChangeToPureOperator(simplified()->NumberEqual());
932 } else if (r.IsReceiverCompareOperation()) {
933 // For strict equality, it's enough to know that one input is a Receiver,
934 // as a strict equality comparison with a Receiver can only yield true if
935 // both sides refer to the same Receiver.
936 r.CheckLeftInputToReceiver();
937 return r.ChangeToPureOperator(simplified()->ReferenceEqual());
938 } else if (r.IsReceiverOrNullOrUndefinedCompareOperation()) {
939 // For strict equality, it's enough to know that one input is a Receiver,
940 // Null or Undefined, as a strict equality comparison with a Receiver,
941 // Null or Undefined can only yield true if both sides refer to the same
942 // instance.
943 r.CheckLeftInputToReceiverOrNullOrUndefined();
944 return r.ChangeToPureOperator(simplified()->ReferenceEqual());
945 } else if (r.IsStringCompareOperation()) {
946 r.CheckInputsToString();
947 return r.ChangeToPureOperator(simplified()->StringEqual());
948 } else if (r.IsSymbolCompareOperation()) {
949 // For strict equality, it's enough to know that one input is a Symbol,
950 // as a strict equality comparison with a Symbol can only yield true if
951 // both sides refer to the same Symbol.
952 r.CheckLeftInputToSymbol();
953 return r.ChangeToPureOperator(simplified()->ReferenceEqual());
954 }
955 return NoChange();
956 }
957
ReduceJSToName(Node * node)958 Reduction JSTypedLowering::ReduceJSToName(Node* node) {
959 Node* const input = NodeProperties::GetValueInput(node, 0);
960 Type const input_type = NodeProperties::GetType(input);
961 if (input_type.Is(Type::Name())) {
962 // JSToName(x:name) => x
963 ReplaceWithValue(node, input);
964 return Replace(input);
965 }
966 return NoChange();
967 }
968
ReduceJSToLength(Node * node)969 Reduction JSTypedLowering::ReduceJSToLength(Node* node) {
970 Node* input = NodeProperties::GetValueInput(node, 0);
971 Type input_type = NodeProperties::GetType(input);
972 if (input_type.Is(type_cache_->kIntegerOrMinusZero)) {
973 if (input_type.IsNone() || input_type.Max() <= 0.0) {
974 input = jsgraph()->ZeroConstant();
975 } else if (input_type.Min() >= kMaxSafeInteger) {
976 input = jsgraph()->Constant(kMaxSafeInteger);
977 } else {
978 if (input_type.Min() <= 0.0) {
979 input = graph()->NewNode(simplified()->NumberMax(),
980 jsgraph()->ZeroConstant(), input);
981 }
982 if (input_type.Max() > kMaxSafeInteger) {
983 input = graph()->NewNode(simplified()->NumberMin(),
984 jsgraph()->Constant(kMaxSafeInteger), input);
985 }
986 }
987 ReplaceWithValue(node, input);
988 return Replace(input);
989 }
990 return NoChange();
991 }
992
ReduceJSToNumberInput(Node * input)993 Reduction JSTypedLowering::ReduceJSToNumberInput(Node* input) {
994 // Try constant-folding of JSToNumber with constant inputs.
995 Type input_type = NodeProperties::GetType(input);
996
997 if (input_type.Is(Type::String())) {
998 HeapObjectMatcher m(input);
999 if (m.HasResolvedValue() && m.Ref(broker()).IsString()) {
1000 StringRef input_value = m.Ref(broker()).AsString();
1001 base::Optional<double> number = input_value.ToNumber();
1002 if (!number.has_value()) return NoChange();
1003 return Replace(jsgraph()->Constant(number.value()));
1004 }
1005 }
1006 if (input_type.IsHeapConstant()) {
1007 HeapObjectRef input_value = input_type.AsHeapConstant()->Ref();
1008 double value;
1009 if (input_value.OddballToNumber().To(&value)) {
1010 return Replace(jsgraph()->Constant(value));
1011 }
1012 }
1013 if (input_type.Is(Type::Number())) {
1014 // JSToNumber(x:number) => x
1015 return Changed(input);
1016 }
1017 if (input_type.Is(Type::Undefined())) {
1018 // JSToNumber(undefined) => #NaN
1019 return Replace(jsgraph()->NaNConstant());
1020 }
1021 if (input_type.Is(Type::Null())) {
1022 // JSToNumber(null) => #0
1023 return Replace(jsgraph()->ZeroConstant());
1024 }
1025 return NoChange();
1026 }
1027
ReduceJSToNumber(Node * node)1028 Reduction JSTypedLowering::ReduceJSToNumber(Node* node) {
1029 // Try to reduce the input first.
1030 Node* const input = node->InputAt(0);
1031 Reduction reduction = ReduceJSToNumberInput(input);
1032 if (reduction.Changed()) {
1033 ReplaceWithValue(node, reduction.replacement());
1034 return reduction;
1035 }
1036 Type const input_type = NodeProperties::GetType(input);
1037 if (input_type.Is(Type::PlainPrimitive())) {
1038 RelaxEffectsAndControls(node);
1039 node->TrimInputCount(1);
1040 // For a PlainPrimitive, ToNumeric is the same as ToNumber.
1041 Type node_type = NodeProperties::GetType(node);
1042 NodeProperties::SetType(
1043 node, Type::Intersect(node_type, Type::Number(), graph()->zone()));
1044 NodeProperties::ChangeOp(node, simplified()->PlainPrimitiveToNumber());
1045 return Changed(node);
1046 }
1047 return NoChange();
1048 }
1049
ReduceJSToNumeric(Node * node)1050 Reduction JSTypedLowering::ReduceJSToNumeric(Node* node) {
1051 Node* const input = NodeProperties::GetValueInput(node, 0);
1052 Type const input_type = NodeProperties::GetType(input);
1053 if (input_type.Is(Type::NonBigIntPrimitive())) {
1054 // ToNumeric(x:primitive\bigint) => ToNumber(x)
1055 NodeProperties::ChangeOp(node, javascript()->ToNumber());
1056 Type node_type = NodeProperties::GetType(node);
1057 NodeProperties::SetType(
1058 node, Type::Intersect(node_type, Type::Number(), graph()->zone()));
1059 return Changed(node).FollowedBy(ReduceJSToNumber(node));
1060 }
1061 return NoChange();
1062 }
1063
ReduceJSToStringInput(Node * input)1064 Reduction JSTypedLowering::ReduceJSToStringInput(Node* input) {
1065 if (input->opcode() == IrOpcode::kJSToString) {
1066 // Recursively try to reduce the input first.
1067 Reduction result = ReduceJSToString(input);
1068 if (result.Changed()) return result;
1069 return Changed(input); // JSToString(JSToString(x)) => JSToString(x)
1070 }
1071 Type input_type = NodeProperties::GetType(input);
1072 if (input_type.Is(Type::String())) {
1073 return Changed(input); // JSToString(x:string) => x
1074 }
1075 if (input_type.Is(Type::Boolean())) {
1076 return Replace(graph()->NewNode(
1077 common()->Select(MachineRepresentation::kTagged), input,
1078 jsgraph()->HeapConstant(factory()->true_string()),
1079 jsgraph()->HeapConstant(factory()->false_string())));
1080 }
1081 if (input_type.Is(Type::Undefined())) {
1082 return Replace(jsgraph()->HeapConstant(factory()->undefined_string()));
1083 }
1084 if (input_type.Is(Type::Null())) {
1085 return Replace(jsgraph()->HeapConstant(factory()->null_string()));
1086 }
1087 if (input_type.Is(Type::NaN())) {
1088 return Replace(jsgraph()->HeapConstant(factory()->NaN_string()));
1089 }
1090 if (input_type.Is(Type::Number())) {
1091 return Replace(graph()->NewNode(simplified()->NumberToString(), input));
1092 }
1093 return NoChange();
1094 }
1095
ReduceJSToString(Node * node)1096 Reduction JSTypedLowering::ReduceJSToString(Node* node) {
1097 DCHECK_EQ(IrOpcode::kJSToString, node->opcode());
1098 // Try to reduce the input first.
1099 Node* const input = node->InputAt(0);
1100 Reduction reduction = ReduceJSToStringInput(input);
1101 if (reduction.Changed()) {
1102 ReplaceWithValue(node, reduction.replacement());
1103 return reduction;
1104 }
1105 return NoChange();
1106 }
1107
ReduceJSToObject(Node * node)1108 Reduction JSTypedLowering::ReduceJSToObject(Node* node) {
1109 DCHECK_EQ(IrOpcode::kJSToObject, node->opcode());
1110 Node* receiver = NodeProperties::GetValueInput(node, 0);
1111 Type receiver_type = NodeProperties::GetType(receiver);
1112 Node* context = NodeProperties::GetContextInput(node);
1113 Node* frame_state = NodeProperties::GetFrameStateInput(node);
1114 Node* effect = NodeProperties::GetEffectInput(node);
1115 Node* control = NodeProperties::GetControlInput(node);
1116 if (receiver_type.Is(Type::Receiver())) {
1117 ReplaceWithValue(node, receiver, effect, control);
1118 return Replace(receiver);
1119 }
1120
1121 // Check whether {receiver} is a spec object.
1122 Node* check = graph()->NewNode(simplified()->ObjectIsReceiver(), receiver);
1123 Node* branch =
1124 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
1125
1126 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
1127 Node* etrue = effect;
1128 Node* rtrue = receiver;
1129
1130 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
1131 Node* efalse = effect;
1132 Node* rfalse;
1133 {
1134 // Convert {receiver} using the ToObjectStub.
1135 Callable callable = Builtins::CallableFor(isolate(), Builtin::kToObject);
1136 auto call_descriptor = Linkage::GetStubCallDescriptor(
1137 graph()->zone(), callable.descriptor(),
1138 callable.descriptor().GetStackParameterCount(),
1139 CallDescriptor::kNeedsFrameState, node->op()->properties());
1140 rfalse = efalse = if_false =
1141 graph()->NewNode(common()->Call(call_descriptor),
1142 jsgraph()->HeapConstant(callable.code()), receiver,
1143 context, frame_state, efalse, if_false);
1144 }
1145
1146 // Update potential {IfException} uses of {node} to point to the above
1147 // ToObject stub call node instead. Note that the stub can only throw on
1148 // receivers that can be null or undefined.
1149 Node* on_exception = nullptr;
1150 if (receiver_type.Maybe(Type::NullOrUndefined()) &&
1151 NodeProperties::IsExceptionalCall(node, &on_exception)) {
1152 NodeProperties::ReplaceControlInput(on_exception, if_false);
1153 NodeProperties::ReplaceEffectInput(on_exception, efalse);
1154 if_false = graph()->NewNode(common()->IfSuccess(), if_false);
1155 Revisit(on_exception);
1156 }
1157
1158 control = graph()->NewNode(common()->Merge(2), if_true, if_false);
1159 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
1160
1161 // Morph the {node} into an appropriate Phi.
1162 ReplaceWithValue(node, node, effect, control);
1163 node->ReplaceInput(0, rtrue);
1164 node->ReplaceInput(1, rfalse);
1165 node->ReplaceInput(2, control);
1166 node->TrimInputCount(3);
1167 NodeProperties::ChangeOp(node,
1168 common()->Phi(MachineRepresentation::kTagged, 2));
1169 return Changed(node);
1170 }
1171
ReduceJSLoadNamed(Node * node)1172 Reduction JSTypedLowering::ReduceJSLoadNamed(Node* node) {
1173 JSLoadNamedNode n(node);
1174 Node* receiver = n.object();
1175 Type receiver_type = NodeProperties::GetType(receiver);
1176 NameRef name = NamedAccessOf(node->op()).name(broker());
1177 NameRef length_str = MakeRef(broker(), factory()->length_string());
1178 // Optimize "length" property of strings.
1179 if (name.equals(length_str) && receiver_type.Is(Type::String())) {
1180 Node* value = graph()->NewNode(simplified()->StringLength(), receiver);
1181 ReplaceWithValue(node, value);
1182 return Replace(value);
1183 }
1184 return NoChange();
1185 }
1186
ReduceJSHasInPrototypeChain(Node * node)1187 Reduction JSTypedLowering::ReduceJSHasInPrototypeChain(Node* node) {
1188 DCHECK_EQ(IrOpcode::kJSHasInPrototypeChain, node->opcode());
1189 Node* value = NodeProperties::GetValueInput(node, 0);
1190 Type value_type = NodeProperties::GetType(value);
1191 Node* prototype = NodeProperties::GetValueInput(node, 1);
1192 Node* context = NodeProperties::GetContextInput(node);
1193 Node* frame_state = NodeProperties::GetFrameStateInput(node);
1194 Node* effect = NodeProperties::GetEffectInput(node);
1195 Node* control = NodeProperties::GetControlInput(node);
1196
1197 // If {value} cannot be a receiver, then it cannot have {prototype} in
1198 // it's prototype chain (all Primitive values have a null prototype).
1199 if (value_type.Is(Type::Primitive())) {
1200 Node* value = jsgraph()->FalseConstant();
1201 ReplaceWithValue(node, value, effect, control);
1202 return Replace(value);
1203 }
1204
1205 Node* check0 = graph()->NewNode(simplified()->ObjectIsSmi(), value);
1206 Node* branch0 =
1207 graph()->NewNode(common()->Branch(BranchHint::kFalse), check0, control);
1208
1209 Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
1210 Node* etrue0 = effect;
1211 Node* vtrue0 = jsgraph()->FalseConstant();
1212
1213 control = graph()->NewNode(common()->IfFalse(), branch0);
1214
1215 // Loop through the {value}s prototype chain looking for the {prototype}.
1216 Node* loop = control = graph()->NewNode(common()->Loop(2), control, control);
1217 Node* eloop = effect =
1218 graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
1219 Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
1220 NodeProperties::MergeControlToEnd(graph(), common(), terminate);
1221 Node* vloop = value = graph()->NewNode(
1222 common()->Phi(MachineRepresentation::kTagged, 2), value, value, loop);
1223 NodeProperties::SetType(vloop, Type::NonInternal());
1224
1225 // Load the {value} map and instance type.
1226 Node* value_map = effect = graph()->NewNode(
1227 simplified()->LoadField(AccessBuilder::ForMap()), value, effect, control);
1228 Node* value_instance_type = effect = graph()->NewNode(
1229 simplified()->LoadField(AccessBuilder::ForMapInstanceType()), value_map,
1230 effect, control);
1231
1232 // Check if the {value} is a special receiver, because for special
1233 // receivers, i.e. proxies or API values that need access checks,
1234 // we have to use the %HasInPrototypeChain runtime function instead.
1235 Node* check1 = graph()->NewNode(
1236 simplified()->NumberLessThanOrEqual(), value_instance_type,
1237 jsgraph()->Constant(LAST_SPECIAL_RECEIVER_TYPE));
1238 Node* branch1 =
1239 graph()->NewNode(common()->Branch(BranchHint::kFalse), check1, control);
1240
1241 control = graph()->NewNode(common()->IfFalse(), branch1);
1242
1243 Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
1244 Node* etrue1 = effect;
1245 Node* vtrue1;
1246
1247 // Check if the {value} is not a receiver at all.
1248 Node* check10 =
1249 graph()->NewNode(simplified()->NumberLessThan(), value_instance_type,
1250 jsgraph()->Constant(FIRST_JS_RECEIVER_TYPE));
1251 Node* branch10 =
1252 graph()->NewNode(common()->Branch(BranchHint::kTrue), check10, if_true1);
1253
1254 // A primitive value cannot match the {prototype} we're looking for.
1255 if_true1 = graph()->NewNode(common()->IfTrue(), branch10);
1256 vtrue1 = jsgraph()->FalseConstant();
1257
1258 Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch10);
1259 Node* efalse1 = etrue1;
1260 Node* vfalse1;
1261 {
1262 // Slow path, need to call the %HasInPrototypeChain runtime function.
1263 vfalse1 = efalse1 = if_false1 = graph()->NewNode(
1264 javascript()->CallRuntime(Runtime::kHasInPrototypeChain), value,
1265 prototype, context, frame_state, efalse1, if_false1);
1266
1267 // Replace any potential {IfException} uses of {node} to catch
1268 // exceptions from this %HasInPrototypeChain runtime call instead.
1269 Node* on_exception = nullptr;
1270 if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
1271 NodeProperties::ReplaceControlInput(on_exception, vfalse1);
1272 NodeProperties::ReplaceEffectInput(on_exception, efalse1);
1273 if_false1 = graph()->NewNode(common()->IfSuccess(), vfalse1);
1274 Revisit(on_exception);
1275 }
1276 }
1277
1278 // Load the {value} prototype.
1279 Node* value_prototype = effect = graph()->NewNode(
1280 simplified()->LoadField(AccessBuilder::ForMapPrototype()), value_map,
1281 effect, control);
1282
1283 // Check if we reached the end of {value}s prototype chain.
1284 Node* check2 = graph()->NewNode(simplified()->ReferenceEqual(),
1285 value_prototype, jsgraph()->NullConstant());
1286 Node* branch2 = graph()->NewNode(common()->Branch(), check2, control);
1287
1288 Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2);
1289 Node* etrue2 = effect;
1290 Node* vtrue2 = jsgraph()->FalseConstant();
1291
1292 control = graph()->NewNode(common()->IfFalse(), branch2);
1293
1294 // Check if we reached the {prototype}.
1295 Node* check3 = graph()->NewNode(simplified()->ReferenceEqual(),
1296 value_prototype, prototype);
1297 Node* branch3 = graph()->NewNode(common()->Branch(), check3, control);
1298
1299 Node* if_true3 = graph()->NewNode(common()->IfTrue(), branch3);
1300 Node* etrue3 = effect;
1301 Node* vtrue3 = jsgraph()->TrueConstant();
1302
1303 control = graph()->NewNode(common()->IfFalse(), branch3);
1304
1305 // Close the loop.
1306 vloop->ReplaceInput(1, value_prototype);
1307 eloop->ReplaceInput(1, effect);
1308 loop->ReplaceInput(1, control);
1309
1310 control = graph()->NewNode(common()->Merge(5), if_true0, if_true1, if_true2,
1311 if_true3, if_false1);
1312 effect = graph()->NewNode(common()->EffectPhi(5), etrue0, etrue1, etrue2,
1313 etrue3, efalse1, control);
1314
1315 // Morph the {node} into an appropriate Phi.
1316 ReplaceWithValue(node, node, effect, control);
1317 node->ReplaceInput(0, vtrue0);
1318 node->ReplaceInput(1, vtrue1);
1319 node->ReplaceInput(2, vtrue2);
1320 node->ReplaceInput(3, vtrue3);
1321 node->ReplaceInput(4, vfalse1);
1322 node->ReplaceInput(5, control);
1323 node->TrimInputCount(6);
1324 NodeProperties::ChangeOp(node,
1325 common()->Phi(MachineRepresentation::kTagged, 5));
1326 return Changed(node);
1327 }
1328
ReduceJSOrdinaryHasInstance(Node * node)1329 Reduction JSTypedLowering::ReduceJSOrdinaryHasInstance(Node* node) {
1330 DCHECK_EQ(IrOpcode::kJSOrdinaryHasInstance, node->opcode());
1331 Node* constructor = NodeProperties::GetValueInput(node, 0);
1332 Type constructor_type = NodeProperties::GetType(constructor);
1333 Node* object = NodeProperties::GetValueInput(node, 1);
1334 Type object_type = NodeProperties::GetType(object);
1335
1336 // Check if the {constructor} cannot be callable.
1337 // See ES6 section 7.3.19 OrdinaryHasInstance ( C, O ) step 1.
1338 if (!constructor_type.Maybe(Type::Callable())) {
1339 Node* value = jsgraph()->FalseConstant();
1340 ReplaceWithValue(node, value);
1341 return Replace(value);
1342 }
1343
1344 // If the {constructor} cannot be a JSBoundFunction and then {object}
1345 // cannot be a JSReceiver, then this can be constant-folded to false.
1346 // See ES6 section 7.3.19 OrdinaryHasInstance ( C, O ) step 2 and 3.
1347 if (!object_type.Maybe(Type::Receiver()) &&
1348 !constructor_type.Maybe(Type::BoundFunction())) {
1349 Node* value = jsgraph()->FalseConstant();
1350 ReplaceWithValue(node, value);
1351 return Replace(value);
1352 }
1353
1354 return NoChange();
1355 }
1356
ReduceJSHasContextExtension(Node * node)1357 Reduction JSTypedLowering::ReduceJSHasContextExtension(Node* node) {
1358 DCHECK_EQ(IrOpcode::kJSHasContextExtension, node->opcode());
1359 size_t depth = OpParameter<size_t>(node->op());
1360 Node* effect = NodeProperties::GetEffectInput(node);
1361 Node* context = NodeProperties::GetContextInput(node);
1362 Node* control = graph()->start();
1363 for (size_t i = 0; i < depth; ++i) {
1364 context = effect = graph()->NewNode(
1365 simplified()->LoadField(
1366 AccessBuilder::ForContextSlotKnownPointer(Context::PREVIOUS_INDEX)),
1367 context, effect, control);
1368 }
1369 Node* const scope_info = effect = graph()->NewNode(
1370 simplified()->LoadField(
1371 AccessBuilder::ForContextSlot(Context::SCOPE_INFO_INDEX)),
1372 context, effect, control);
1373 Node* scope_info_flags = effect = graph()->NewNode(
1374 simplified()->LoadField(AccessBuilder::ForScopeInfoFlags()), scope_info,
1375 effect, control);
1376 Node* flags_masked = graph()->NewNode(
1377 simplified()->NumberBitwiseAnd(), scope_info_flags,
1378 jsgraph()->SmiConstant(ScopeInfo::HasContextExtensionSlotBit::kMask));
1379 Node* no_extension = graph()->NewNode(
1380 simplified()->NumberEqual(), flags_masked, jsgraph()->SmiConstant(0));
1381 Node* has_extension =
1382 graph()->NewNode(simplified()->BooleanNot(), no_extension);
1383 ReplaceWithValue(node, has_extension, effect, control);
1384 return Changed(node);
1385 }
1386
ReduceJSLoadContext(Node * node)1387 Reduction JSTypedLowering::ReduceJSLoadContext(Node* node) {
1388 DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
1389 ContextAccess const& access = ContextAccessOf(node->op());
1390 Node* effect = NodeProperties::GetEffectInput(node);
1391 Node* context = NodeProperties::GetContextInput(node);
1392 Node* control = graph()->start();
1393 for (size_t i = 0; i < access.depth(); ++i) {
1394 context = effect = graph()->NewNode(
1395 simplified()->LoadField(
1396 AccessBuilder::ForContextSlotKnownPointer(Context::PREVIOUS_INDEX)),
1397 context, effect, control);
1398 }
1399 node->ReplaceInput(0, context);
1400 node->ReplaceInput(1, effect);
1401 node->AppendInput(jsgraph()->zone(), control);
1402 NodeProperties::ChangeOp(
1403 node,
1404 simplified()->LoadField(AccessBuilder::ForContextSlot(access.index())));
1405 return Changed(node);
1406 }
1407
ReduceJSStoreContext(Node * node)1408 Reduction JSTypedLowering::ReduceJSStoreContext(Node* node) {
1409 DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());
1410 ContextAccess const& access = ContextAccessOf(node->op());
1411 Node* effect = NodeProperties::GetEffectInput(node);
1412 Node* context = NodeProperties::GetContextInput(node);
1413 Node* control = graph()->start();
1414 Node* value = NodeProperties::GetValueInput(node, 0);
1415 for (size_t i = 0; i < access.depth(); ++i) {
1416 context = effect = graph()->NewNode(
1417 simplified()->LoadField(
1418 AccessBuilder::ForContextSlotKnownPointer(Context::PREVIOUS_INDEX)),
1419 context, effect, control);
1420 }
1421 node->ReplaceInput(0, context);
1422 node->ReplaceInput(1, value);
1423 node->ReplaceInput(2, effect);
1424 NodeProperties::ChangeOp(
1425 node,
1426 simplified()->StoreField(AccessBuilder::ForContextSlot(access.index())));
1427 return Changed(node);
1428 }
1429
BuildGetModuleCell(Node * node)1430 Node* JSTypedLowering::BuildGetModuleCell(Node* node) {
1431 DCHECK(node->opcode() == IrOpcode::kJSLoadModule ||
1432 node->opcode() == IrOpcode::kJSStoreModule);
1433 Node* effect = NodeProperties::GetEffectInput(node);
1434 Node* control = NodeProperties::GetControlInput(node);
1435
1436 int32_t cell_index = OpParameter<int32_t>(node->op());
1437 Node* module = NodeProperties::GetValueInput(node, 0);
1438 Type module_type = NodeProperties::GetType(module);
1439
1440 if (module_type.IsHeapConstant()) {
1441 SourceTextModuleRef module_constant =
1442 module_type.AsHeapConstant()->Ref().AsSourceTextModule();
1443 base::Optional<CellRef> cell_constant = module_constant.GetCell(cell_index);
1444 if (cell_constant.has_value()) return jsgraph()->Constant(*cell_constant);
1445 }
1446
1447 FieldAccess field_access;
1448 int index;
1449 if (SourceTextModuleDescriptor::GetCellIndexKind(cell_index) ==
1450 SourceTextModuleDescriptor::kExport) {
1451 field_access = AccessBuilder::ForModuleRegularExports();
1452 index = cell_index - 1;
1453 } else {
1454 DCHECK_EQ(SourceTextModuleDescriptor::GetCellIndexKind(cell_index),
1455 SourceTextModuleDescriptor::kImport);
1456 field_access = AccessBuilder::ForModuleRegularImports();
1457 index = -cell_index - 1;
1458 }
1459 Node* array = effect = graph()->NewNode(simplified()->LoadField(field_access),
1460 module, effect, control);
1461 return graph()->NewNode(
1462 simplified()->LoadField(AccessBuilder::ForFixedArraySlot(index)), array,
1463 effect, control);
1464 }
1465
ReduceJSLoadModule(Node * node)1466 Reduction JSTypedLowering::ReduceJSLoadModule(Node* node) {
1467 DCHECK_EQ(IrOpcode::kJSLoadModule, node->opcode());
1468 Node* effect = NodeProperties::GetEffectInput(node);
1469 Node* control = NodeProperties::GetControlInput(node);
1470
1471 Node* cell = BuildGetModuleCell(node);
1472 if (cell->op()->EffectOutputCount() > 0) effect = cell;
1473 Node* value = effect =
1474 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForCellValue()),
1475 cell, effect, control);
1476
1477 ReplaceWithValue(node, value, effect, control);
1478 return Changed(value);
1479 }
1480
ReduceJSStoreModule(Node * node)1481 Reduction JSTypedLowering::ReduceJSStoreModule(Node* node) {
1482 DCHECK_EQ(IrOpcode::kJSStoreModule, node->opcode());
1483 Node* effect = NodeProperties::GetEffectInput(node);
1484 Node* control = NodeProperties::GetControlInput(node);
1485 Node* value = NodeProperties::GetValueInput(node, 1);
1486 DCHECK_EQ(SourceTextModuleDescriptor::GetCellIndexKind(
1487 OpParameter<int32_t>(node->op())),
1488 SourceTextModuleDescriptor::kExport);
1489
1490 Node* cell = BuildGetModuleCell(node);
1491 if (cell->op()->EffectOutputCount() > 0) effect = cell;
1492 effect =
1493 graph()->NewNode(simplified()->StoreField(AccessBuilder::ForCellValue()),
1494 cell, value, effect, control);
1495
1496 ReplaceWithValue(node, effect, effect, control);
1497 return Changed(value);
1498 }
1499
1500 namespace {
1501
ReduceBuiltin(JSGraph * jsgraph,Node * node,Builtin builtin,int arity,CallDescriptor::Flags flags)1502 void ReduceBuiltin(JSGraph* jsgraph, Node* node, Builtin builtin, int arity,
1503 CallDescriptor::Flags flags) {
1504 // Patch {node} to a direct CEntry call.
1505 // ----------- A r g u m e n t s -----------
1506 // -- 0: CEntry
1507 // --- Stack args ---
1508 // -- 1: new_target
1509 // -- 2: target
1510 // -- 3: argc, including the receiver and implicit args (Smi)
1511 // -- 4: padding
1512 // -- 5: receiver
1513 // -- [6, 6 + n[: the n actual arguments passed to the builtin
1514 // --- Register args ---
1515 // -- 6 + n: the C entry point
1516 // -- 6 + n + 1: argc (Int32)
1517 // -----------------------------------
1518
1519 // The logic contained here is mirrored in Builtins::Generate_Adaptor.
1520 // Keep these in sync.
1521
1522 Node* target = node->InputAt(JSCallOrConstructNode::TargetIndex());
1523
1524 // Unify representations between construct and call nodes. For construct
1525 // nodes, the receiver is undefined. For call nodes, the new_target is
1526 // undefined.
1527 Node* new_target;
1528 Zone* zone = jsgraph->zone();
1529 if (node->opcode() == IrOpcode::kJSConstruct) {
1530 STATIC_ASSERT(JSCallNode::ReceiverIndex() ==
1531 JSConstructNode::NewTargetIndex());
1532 new_target = JSConstructNode{node}.new_target();
1533 node->ReplaceInput(JSConstructNode::NewTargetIndex(),
1534 jsgraph->UndefinedConstant());
1535 node->RemoveInput(JSConstructNode{node}.FeedbackVectorIndex());
1536 } else {
1537 new_target = jsgraph->UndefinedConstant();
1538 node->RemoveInput(JSCallNode{node}.FeedbackVectorIndex());
1539 }
1540
1541 // CPP builtins are implemented in C++, and we can inline it.
1542 // CPP builtins create a builtin exit frame.
1543 DCHECK(Builtins::IsCpp(builtin));
1544 const bool has_builtin_exit_frame = true;
1545
1546 Node* stub = jsgraph->CEntryStubConstant(
1547 1, SaveFPRegsMode::kIgnore, ArgvMode::kStack, has_builtin_exit_frame);
1548 node->ReplaceInput(0, stub);
1549
1550 const int argc = arity + BuiltinArguments::kNumExtraArgsWithReceiver;
1551 Node* argc_node = jsgraph->Constant(argc);
1552
1553 static const int kStubAndReceiver = 2;
1554 node->InsertInput(zone, 1, new_target);
1555 node->InsertInput(zone, 2, target);
1556 node->InsertInput(zone, 3, argc_node);
1557 node->InsertInput(zone, 4, jsgraph->PaddingConstant());
1558 int cursor = arity + kStubAndReceiver + BuiltinArguments::kNumExtraArgs;
1559
1560 Address entry = Builtins::CppEntryOf(builtin);
1561 ExternalReference entry_ref = ExternalReference::Create(entry);
1562 Node* entry_node = jsgraph->ExternalConstant(entry_ref);
1563
1564 node->InsertInput(zone, cursor++, entry_node);
1565 node->InsertInput(zone, cursor++, argc_node);
1566
1567 static const int kReturnCount = 1;
1568 const char* debug_name = Builtins::name(builtin);
1569 Operator::Properties properties = node->op()->properties();
1570 auto call_descriptor = Linkage::GetCEntryStubCallDescriptor(
1571 zone, kReturnCount, argc, debug_name, properties, flags,
1572 StackArgumentOrder::kJS);
1573
1574 NodeProperties::ChangeOp(node, jsgraph->common()->Call(call_descriptor));
1575 }
1576 } // namespace
1577
ReduceJSConstructForwardVarargs(Node * node)1578 Reduction JSTypedLowering::ReduceJSConstructForwardVarargs(Node* node) {
1579 DCHECK_EQ(IrOpcode::kJSConstructForwardVarargs, node->opcode());
1580 ConstructForwardVarargsParameters p =
1581 ConstructForwardVarargsParametersOf(node->op());
1582 DCHECK_LE(2u, p.arity());
1583 int const arity = static_cast<int>(p.arity() - 2);
1584 int const start_index = static_cast<int>(p.start_index());
1585 Node* target = NodeProperties::GetValueInput(node, 0);
1586 Type target_type = NodeProperties::GetType(target);
1587
1588 // Check if {target} is a JSFunction.
1589 if (target_type.IsHeapConstant() &&
1590 target_type.AsHeapConstant()->Ref().IsJSFunction()) {
1591 // Only optimize [[Construct]] here if {function} is a Constructor.
1592 JSFunctionRef function = target_type.AsHeapConstant()->Ref().AsJSFunction();
1593 if (!function.map().is_constructor()) return NoChange();
1594 // Patch {node} to an indirect call via ConstructFunctionForwardVarargs.
1595 Callable callable = CodeFactory::ConstructFunctionForwardVarargs(isolate());
1596 node->InsertInput(graph()->zone(), 0,
1597 jsgraph()->HeapConstant(callable.code()));
1598 node->InsertInput(graph()->zone(), 3,
1599 jsgraph()->Constant(JSParameterCount(arity)));
1600 node->InsertInput(graph()->zone(), 4, jsgraph()->Constant(start_index));
1601 node->InsertInput(graph()->zone(), 5, jsgraph()->UndefinedConstant());
1602 NodeProperties::ChangeOp(
1603 node, common()->Call(Linkage::GetStubCallDescriptor(
1604 graph()->zone(), callable.descriptor(), arity + 1,
1605 CallDescriptor::kNeedsFrameState)));
1606 return Changed(node);
1607 }
1608
1609 return NoChange();
1610 }
1611
ReduceJSConstruct(Node * node)1612 Reduction JSTypedLowering::ReduceJSConstruct(Node* node) {
1613 JSConstructNode n(node);
1614 ConstructParameters const& p = n.Parameters();
1615 int const arity = p.arity_without_implicit_args();
1616 Node* target = n.target();
1617 Type target_type = NodeProperties::GetType(target);
1618
1619 // Check if {target} is a known JSFunction.
1620 if (target_type.IsHeapConstant() &&
1621 target_type.AsHeapConstant()->Ref().IsJSFunction()) {
1622 JSFunctionRef function = target_type.AsHeapConstant()->Ref().AsJSFunction();
1623
1624 // Only optimize [[Construct]] here if {function} is a Constructor.
1625 if (!function.map().is_constructor()) return NoChange();
1626
1627 // Patch {node} to an indirect call via the {function}s construct stub.
1628 bool use_builtin_construct_stub = function.shared().construct_as_builtin();
1629 CodeRef code = MakeRef(
1630 broker(), use_builtin_construct_stub
1631 ? BUILTIN_CODE(isolate(), JSBuiltinsConstructStub)
1632 : BUILTIN_CODE(isolate(), JSConstructStubGeneric));
1633 STATIC_ASSERT(JSConstructNode::TargetIndex() == 0);
1634 STATIC_ASSERT(JSConstructNode::NewTargetIndex() == 1);
1635 node->RemoveInput(n.FeedbackVectorIndex());
1636 node->InsertInput(graph()->zone(), 0, jsgraph()->Constant(code));
1637 node->InsertInput(graph()->zone(), 3,
1638 jsgraph()->Constant(JSParameterCount(arity)));
1639 node->InsertInput(graph()->zone(), 4, jsgraph()->UndefinedConstant());
1640 node->InsertInput(graph()->zone(), 5, jsgraph()->UndefinedConstant());
1641 NodeProperties::ChangeOp(
1642 node, common()->Call(Linkage::GetStubCallDescriptor(
1643 graph()->zone(), ConstructStubDescriptor{}, 1 + arity,
1644 CallDescriptor::kNeedsFrameState)));
1645 return Changed(node);
1646 }
1647
1648 return NoChange();
1649 }
1650
ReduceJSCallForwardVarargs(Node * node)1651 Reduction JSTypedLowering::ReduceJSCallForwardVarargs(Node* node) {
1652 DCHECK_EQ(IrOpcode::kJSCallForwardVarargs, node->opcode());
1653 CallForwardVarargsParameters p = CallForwardVarargsParametersOf(node->op());
1654 DCHECK_LE(2u, p.arity());
1655 int const arity = static_cast<int>(p.arity() - 2);
1656 int const start_index = static_cast<int>(p.start_index());
1657 Node* target = NodeProperties::GetValueInput(node, 0);
1658 Type target_type = NodeProperties::GetType(target);
1659
1660 // Check if {target} is a JSFunction.
1661 if (target_type.Is(Type::Function())) {
1662 // Compute flags for the call.
1663 CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
1664 // Patch {node} to an indirect call via CallFunctionForwardVarargs.
1665 Callable callable = CodeFactory::CallFunctionForwardVarargs(isolate());
1666 node->InsertInput(graph()->zone(), 0,
1667 jsgraph()->HeapConstant(callable.code()));
1668 node->InsertInput(graph()->zone(), 2,
1669 jsgraph()->Constant(JSParameterCount(arity)));
1670 node->InsertInput(graph()->zone(), 3, jsgraph()->Constant(start_index));
1671 NodeProperties::ChangeOp(
1672 node, common()->Call(Linkage::GetStubCallDescriptor(
1673 graph()->zone(), callable.descriptor(), arity + 1, flags)));
1674 return Changed(node);
1675 }
1676
1677 return NoChange();
1678 }
1679
ReduceJSCall(Node * node)1680 Reduction JSTypedLowering::ReduceJSCall(Node* node) {
1681 JSCallNode n(node);
1682 CallParameters const& p = n.Parameters();
1683 int arity = p.arity_without_implicit_args();
1684 ConvertReceiverMode convert_mode = p.convert_mode();
1685 Node* target = n.target();
1686 Type target_type = NodeProperties::GetType(target);
1687 Node* receiver = n.receiver();
1688 Type receiver_type = NodeProperties::GetType(receiver);
1689 Effect effect = n.effect();
1690 Control control = n.control();
1691
1692 // Try to infer receiver {convert_mode} from {receiver} type.
1693 if (receiver_type.Is(Type::NullOrUndefined())) {
1694 convert_mode = ConvertReceiverMode::kNullOrUndefined;
1695 } else if (!receiver_type.Maybe(Type::NullOrUndefined())) {
1696 convert_mode = ConvertReceiverMode::kNotNullOrUndefined;
1697 }
1698
1699 // Check if we know the SharedFunctionInfo of {target}.
1700 base::Optional<JSFunctionRef> function;
1701 base::Optional<SharedFunctionInfoRef> shared;
1702
1703 if (target_type.IsHeapConstant() &&
1704 target_type.AsHeapConstant()->Ref().IsJSFunction()) {
1705 function = target_type.AsHeapConstant()->Ref().AsJSFunction();
1706 shared = function->shared();
1707 } else if (target->opcode() == IrOpcode::kJSCreateClosure) {
1708 CreateClosureParameters const& ccp =
1709 JSCreateClosureNode{target}.Parameters();
1710 shared = ccp.shared_info(broker());
1711 } else if (target->opcode() == IrOpcode::kCheckClosure) {
1712 FeedbackCellRef cell = MakeRef(broker(), FeedbackCellOf(target->op()));
1713 shared = cell.shared_function_info();
1714 }
1715
1716 if (shared.has_value()) {
1717 // Do not inline the call if we need to check whether to break at entry.
1718 // If this state changes during background compilation, the compilation
1719 // job will be aborted from the main thread (see
1720 // Debug::PrepareFunctionForDebugExecution()).
1721 if (shared->HasBreakInfo()) return NoChange();
1722
1723 // Class constructors are callable, but [[Call]] will raise an exception.
1724 // See ES6 section 9.2.1 [[Call]] ( thisArgument, argumentsList ).
1725 if (IsClassConstructor(shared->kind())) return NoChange();
1726
1727 // Check if we need to convert the {receiver}, but bailout if it would
1728 // require data from a foreign native context.
1729 if (is_sloppy(shared->language_mode()) && !shared->native() &&
1730 !receiver_type.Is(Type::Receiver())) {
1731 if (!function.has_value() || !function->native_context().equals(
1732 broker()->target_native_context())) {
1733 return NoChange();
1734 }
1735 Node* global_proxy =
1736 jsgraph()->Constant(function->native_context().global_proxy_object());
1737 receiver = effect =
1738 graph()->NewNode(simplified()->ConvertReceiver(convert_mode),
1739 receiver, global_proxy, effect, control);
1740 NodeProperties::ReplaceValueInput(node, receiver, 1);
1741 }
1742
1743 // Load the context from the {target}.
1744 Node* context = effect = graph()->NewNode(
1745 simplified()->LoadField(AccessBuilder::ForJSFunctionContext()), target,
1746 effect, control);
1747 NodeProperties::ReplaceContextInput(node, context);
1748
1749 // Update the effect dependency for the {node}.
1750 NodeProperties::ReplaceEffectInput(node, effect);
1751
1752 // Compute flags for the call.
1753 CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
1754 Node* new_target = jsgraph()->UndefinedConstant();
1755
1756 int formal_count =
1757 shared->internal_formal_parameter_count_without_receiver();
1758 // TODO(v8:11112): Once the sentinel is always 0, the check against
1759 // IsDontAdaptArguments() can be removed.
1760 if (!shared->IsDontAdaptArguments() && formal_count > arity) {
1761 node->RemoveInput(n.FeedbackVectorIndex());
1762 // Underapplication. Massage the arguments to match the expected number of
1763 // arguments.
1764 for (int i = arity; i < formal_count; i++) {
1765 node->InsertInput(graph()->zone(), arity + 2,
1766 jsgraph()->UndefinedConstant());
1767 }
1768
1769 // Patch {node} to a direct call.
1770 node->InsertInput(graph()->zone(), formal_count + 2, new_target);
1771 node->InsertInput(graph()->zone(), formal_count + 3,
1772 jsgraph()->Constant(JSParameterCount(arity)));
1773 NodeProperties::ChangeOp(node,
1774 common()->Call(Linkage::GetJSCallDescriptor(
1775 graph()->zone(), false, 1 + formal_count,
1776 flags | CallDescriptor::kCanUseRoots)));
1777 } else if (shared->HasBuiltinId() &&
1778 Builtins::IsCpp(shared->builtin_id())) {
1779 // Patch {node} to a direct CEntry call.
1780 ReduceBuiltin(jsgraph(), node, shared->builtin_id(), arity, flags);
1781 } else if (shared->HasBuiltinId()) {
1782 DCHECK(Builtins::HasJSLinkage(shared->builtin_id()));
1783 // Patch {node} to a direct code object call.
1784 Callable callable =
1785 Builtins::CallableFor(isolate(), shared->builtin_id());
1786 CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
1787
1788 const CallInterfaceDescriptor& descriptor = callable.descriptor();
1789 auto call_descriptor = Linkage::GetStubCallDescriptor(
1790 graph()->zone(), descriptor, 1 + arity, flags);
1791 Node* stub_code = jsgraph()->HeapConstant(callable.code());
1792 node->RemoveInput(n.FeedbackVectorIndex());
1793 node->InsertInput(graph()->zone(), 0, stub_code); // Code object.
1794 node->InsertInput(graph()->zone(), 2, new_target);
1795 node->InsertInput(graph()->zone(), 3,
1796 jsgraph()->Constant(JSParameterCount(arity)));
1797 NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
1798 } else {
1799 // Patch {node} to a direct call.
1800 node->RemoveInput(n.FeedbackVectorIndex());
1801 node->InsertInput(graph()->zone(), arity + 2, new_target);
1802 node->InsertInput(graph()->zone(), arity + 3,
1803 jsgraph()->Constant(JSParameterCount(arity)));
1804 NodeProperties::ChangeOp(node,
1805 common()->Call(Linkage::GetJSCallDescriptor(
1806 graph()->zone(), false, 1 + arity,
1807 flags | CallDescriptor::kCanUseRoots)));
1808 }
1809 return Changed(node);
1810 }
1811
1812 // Check if {target} is a JSFunction.
1813 if (target_type.Is(Type::Function())) {
1814 // The node will change operators, remove the feedback vector.
1815 node->RemoveInput(n.FeedbackVectorIndex());
1816 // Compute flags for the call.
1817 CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
1818 // Patch {node} to an indirect call via the CallFunction builtin.
1819 Callable callable = CodeFactory::CallFunction(isolate(), convert_mode);
1820 node->InsertInput(graph()->zone(), 0,
1821 jsgraph()->HeapConstant(callable.code()));
1822 node->InsertInput(graph()->zone(), 2,
1823 jsgraph()->Constant(JSParameterCount(arity)));
1824 NodeProperties::ChangeOp(
1825 node, common()->Call(Linkage::GetStubCallDescriptor(
1826 graph()->zone(), callable.descriptor(), 1 + arity, flags)));
1827 return Changed(node);
1828 }
1829
1830 // Maybe we did at least learn something about the {receiver}.
1831 if (p.convert_mode() != convert_mode) {
1832 NodeProperties::ChangeOp(
1833 node,
1834 javascript()->Call(p.arity(), p.frequency(), p.feedback(), convert_mode,
1835 p.speculation_mode(), p.feedback_relation()));
1836 return Changed(node);
1837 }
1838
1839 return NoChange();
1840 }
1841
ReduceJSForInNext(Node * node)1842 Reduction JSTypedLowering::ReduceJSForInNext(Node* node) {
1843 JSForInNextNode n(node);
1844 Node* receiver = n.receiver();
1845 Node* cache_array = n.cache_array();
1846 Node* cache_type = n.cache_type();
1847 Node* index = n.index();
1848 Node* context = n.context();
1849 FrameState frame_state = n.frame_state();
1850 Effect effect = n.effect();
1851 Control control = n.control();
1852
1853 // Load the map of the {receiver}.
1854 Node* receiver_map = effect =
1855 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
1856 receiver, effect, control);
1857
1858 switch (n.Parameters().mode()) {
1859 case ForInMode::kUseEnumCacheKeys:
1860 case ForInMode::kUseEnumCacheKeysAndIndices: {
1861 // Ensure that the expected map still matches that of the {receiver}.
1862 Node* check = graph()->NewNode(simplified()->ReferenceEqual(),
1863 receiver_map, cache_type);
1864 effect =
1865 graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kWrongMap),
1866 check, effect, control);
1867
1868 // Since the change to LoadElement() below is effectful, we connect
1869 // node to all effect uses.
1870 ReplaceWithValue(node, node, node, control);
1871
1872 // Morph the {node} into a LoadElement.
1873 node->ReplaceInput(0, cache_array);
1874 node->ReplaceInput(1, index);
1875 node->ReplaceInput(2, effect);
1876 node->ReplaceInput(3, control);
1877 node->TrimInputCount(4);
1878 NodeProperties::ChangeOp(
1879 node,
1880 simplified()->LoadElement(AccessBuilder::ForFixedArrayElement()));
1881 NodeProperties::SetType(node, Type::InternalizedString());
1882 break;
1883 }
1884 case ForInMode::kGeneric: {
1885 // Load the next {key} from the {cache_array}.
1886 Node* key = effect = graph()->NewNode(
1887 simplified()->LoadElement(AccessBuilder::ForFixedArrayElement()),
1888 cache_array, index, effect, control);
1889
1890 // Check if the expected map still matches that of the {receiver}.
1891 Node* check = graph()->NewNode(simplified()->ReferenceEqual(),
1892 receiver_map, cache_type);
1893 Node* branch =
1894 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
1895
1896 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
1897 Node* etrue;
1898 Node* vtrue;
1899 {
1900 // Don't need filtering since expected map still matches that of the
1901 // {receiver}.
1902 etrue = effect;
1903 vtrue = key;
1904 }
1905
1906 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
1907 Node* efalse;
1908 Node* vfalse;
1909 {
1910 // Filter the {key} to check if it's still a valid property of the
1911 // {receiver} (does the ToName conversion implicitly).
1912 Callable const callable =
1913 Builtins::CallableFor(isolate(), Builtin::kForInFilter);
1914 auto call_descriptor = Linkage::GetStubCallDescriptor(
1915 graph()->zone(), callable.descriptor(),
1916 callable.descriptor().GetStackParameterCount(),
1917 CallDescriptor::kNeedsFrameState);
1918 vfalse = efalse = if_false =
1919 graph()->NewNode(common()->Call(call_descriptor),
1920 jsgraph()->HeapConstant(callable.code()), key,
1921 receiver, context, frame_state, effect, if_false);
1922 NodeProperties::SetType(
1923 vfalse,
1924 Type::Union(Type::String(), Type::Undefined(), graph()->zone()));
1925
1926 // Update potential {IfException} uses of {node} to point to the above
1927 // ForInFilter stub call node instead.
1928 Node* if_exception = nullptr;
1929 if (NodeProperties::IsExceptionalCall(node, &if_exception)) {
1930 if_false = graph()->NewNode(common()->IfSuccess(), vfalse);
1931 NodeProperties::ReplaceControlInput(if_exception, vfalse);
1932 NodeProperties::ReplaceEffectInput(if_exception, efalse);
1933 Revisit(if_exception);
1934 }
1935 }
1936
1937 control = graph()->NewNode(common()->Merge(2), if_true, if_false);
1938 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
1939 ReplaceWithValue(node, node, effect, control);
1940
1941 // Morph the {node} into a Phi.
1942 node->ReplaceInput(0, vtrue);
1943 node->ReplaceInput(1, vfalse);
1944 node->ReplaceInput(2, control);
1945 node->TrimInputCount(3);
1946 NodeProperties::ChangeOp(
1947 node, common()->Phi(MachineRepresentation::kTagged, 2));
1948 }
1949 }
1950
1951 return Changed(node);
1952 }
1953
ReduceJSForInPrepare(Node * node)1954 Reduction JSTypedLowering::ReduceJSForInPrepare(Node* node) {
1955 JSForInPrepareNode n(node);
1956 Node* enumerator = n.enumerator();
1957 Effect effect = n.effect();
1958 Control control = n.control();
1959 Node* cache_type = enumerator;
1960 Node* cache_array = nullptr;
1961 Node* cache_length = nullptr;
1962
1963 switch (n.Parameters().mode()) {
1964 case ForInMode::kUseEnumCacheKeys:
1965 case ForInMode::kUseEnumCacheKeysAndIndices: {
1966 // Check that the {enumerator} is a Map.
1967 effect = graph()->NewNode(
1968 simplified()->CheckMaps(CheckMapsFlag::kNone,
1969 ZoneHandleSet<Map>(factory()->meta_map())),
1970 enumerator, effect, control);
1971
1972 // Load the enum cache from the {enumerator} map.
1973 Node* descriptor_array = effect = graph()->NewNode(
1974 simplified()->LoadField(AccessBuilder::ForMapDescriptors()),
1975 enumerator, effect, control);
1976 Node* enum_cache = effect = graph()->NewNode(
1977 simplified()->LoadField(AccessBuilder::ForDescriptorArrayEnumCache()),
1978 descriptor_array, effect, control);
1979 cache_array = effect = graph()->NewNode(
1980 simplified()->LoadField(AccessBuilder::ForEnumCacheKeys()),
1981 enum_cache, effect, control);
1982
1983 // Load the enum length of the {enumerator} map.
1984 Node* bit_field3 = effect = graph()->NewNode(
1985 simplified()->LoadField(AccessBuilder::ForMapBitField3()), enumerator,
1986 effect, control);
1987 STATIC_ASSERT(Map::Bits3::EnumLengthBits::kShift == 0);
1988 cache_length = graph()->NewNode(
1989 simplified()->NumberBitwiseAnd(), bit_field3,
1990 jsgraph()->Constant(Map::Bits3::EnumLengthBits::kMask));
1991 break;
1992 }
1993 case ForInMode::kGeneric: {
1994 // Check if the {enumerator} is a Map or a FixedArray.
1995 Node* check = effect = graph()->NewNode(
1996 simplified()->CompareMaps(ZoneHandleSet<Map>(factory()->meta_map())),
1997 enumerator, effect, control);
1998 Node* branch =
1999 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
2000
2001 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
2002 Node* etrue = effect;
2003 Node* cache_array_true;
2004 Node* cache_length_true;
2005 {
2006 // Load the enum cache from the {enumerator} map.
2007 Node* descriptor_array = etrue = graph()->NewNode(
2008 simplified()->LoadField(AccessBuilder::ForMapDescriptors()),
2009 enumerator, etrue, if_true);
2010 Node* enum_cache = etrue =
2011 graph()->NewNode(simplified()->LoadField(
2012 AccessBuilder::ForDescriptorArrayEnumCache()),
2013 descriptor_array, etrue, if_true);
2014 cache_array_true = etrue = graph()->NewNode(
2015 simplified()->LoadField(AccessBuilder::ForEnumCacheKeys()),
2016 enum_cache, etrue, if_true);
2017
2018 // Load the enum length of the {enumerator} map.
2019 Node* bit_field3 = etrue = graph()->NewNode(
2020 simplified()->LoadField(AccessBuilder::ForMapBitField3()),
2021 enumerator, etrue, if_true);
2022 STATIC_ASSERT(Map::Bits3::EnumLengthBits::kShift == 0);
2023 cache_length_true = graph()->NewNode(
2024 simplified()->NumberBitwiseAnd(), bit_field3,
2025 jsgraph()->Constant(Map::Bits3::EnumLengthBits::kMask));
2026 }
2027
2028 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
2029 Node* efalse = effect;
2030 Node* cache_array_false;
2031 Node* cache_length_false;
2032 {
2033 // The {enumerator} is the FixedArray with the keys to iterate.
2034 cache_array_false = enumerator;
2035 cache_length_false = efalse = graph()->NewNode(
2036 simplified()->LoadField(AccessBuilder::ForFixedArrayLength()),
2037 cache_array_false, efalse, if_false);
2038 }
2039
2040 // Rewrite the uses of the {node}.
2041 control = graph()->NewNode(common()->Merge(2), if_true, if_false);
2042 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
2043 cache_array =
2044 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
2045 cache_array_true, cache_array_false, control);
2046 cache_length =
2047 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
2048 cache_length_true, cache_length_false, control);
2049 break;
2050 }
2051 }
2052
2053 // Update the uses of {node}.
2054 for (Edge edge : node->use_edges()) {
2055 Node* const user = edge.from();
2056 if (NodeProperties::IsEffectEdge(edge)) {
2057 edge.UpdateTo(effect);
2058 Revisit(user);
2059 } else if (NodeProperties::IsControlEdge(edge)) {
2060 edge.UpdateTo(control);
2061 Revisit(user);
2062 } else {
2063 DCHECK(NodeProperties::IsValueEdge(edge));
2064 switch (ProjectionIndexOf(user->op())) {
2065 case 0:
2066 Replace(user, cache_type);
2067 break;
2068 case 1:
2069 Replace(user, cache_array);
2070 break;
2071 case 2:
2072 Replace(user, cache_length);
2073 break;
2074 default:
2075 UNREACHABLE();
2076 }
2077 }
2078 }
2079 node->Kill();
2080 return Replace(effect);
2081 }
2082
ReduceJSLoadMessage(Node * node)2083 Reduction JSTypedLowering::ReduceJSLoadMessage(Node* node) {
2084 DCHECK_EQ(IrOpcode::kJSLoadMessage, node->opcode());
2085 ExternalReference const ref =
2086 ExternalReference::address_of_pending_message(isolate());
2087 node->ReplaceInput(0, jsgraph()->ExternalConstant(ref));
2088 NodeProperties::ChangeOp(node, simplified()->LoadMessage());
2089 return Changed(node);
2090 }
2091
ReduceJSStoreMessage(Node * node)2092 Reduction JSTypedLowering::ReduceJSStoreMessage(Node* node) {
2093 DCHECK_EQ(IrOpcode::kJSStoreMessage, node->opcode());
2094 ExternalReference const ref =
2095 ExternalReference::address_of_pending_message(isolate());
2096 Node* value = NodeProperties::GetValueInput(node, 0);
2097 node->ReplaceInput(0, jsgraph()->ExternalConstant(ref));
2098 node->ReplaceInput(1, value);
2099 NodeProperties::ChangeOp(node, simplified()->StoreMessage());
2100 return Changed(node);
2101 }
2102
ReduceJSGeneratorStore(Node * node)2103 Reduction JSTypedLowering::ReduceJSGeneratorStore(Node* node) {
2104 DCHECK_EQ(IrOpcode::kJSGeneratorStore, node->opcode());
2105 Node* generator = NodeProperties::GetValueInput(node, 0);
2106 Node* continuation = NodeProperties::GetValueInput(node, 1);
2107 Node* offset = NodeProperties::GetValueInput(node, 2);
2108 Node* context = NodeProperties::GetContextInput(node);
2109 Node* effect = NodeProperties::GetEffectInput(node);
2110 Node* control = NodeProperties::GetControlInput(node);
2111 int value_count = GeneratorStoreValueCountOf(node->op());
2112
2113 FieldAccess array_field =
2114 AccessBuilder::ForJSGeneratorObjectParametersAndRegisters();
2115 FieldAccess context_field = AccessBuilder::ForJSGeneratorObjectContext();
2116 FieldAccess continuation_field =
2117 AccessBuilder::ForJSGeneratorObjectContinuation();
2118 FieldAccess input_or_debug_pos_field =
2119 AccessBuilder::ForJSGeneratorObjectInputOrDebugPos();
2120
2121 Node* array = effect = graph()->NewNode(simplified()->LoadField(array_field),
2122 generator, effect, control);
2123
2124 for (int i = 0; i < value_count; ++i) {
2125 Node* value = NodeProperties::GetValueInput(node, 3 + i);
2126 if (value != jsgraph()->OptimizedOutConstant()) {
2127 effect = graph()->NewNode(
2128 simplified()->StoreField(AccessBuilder::ForFixedArraySlot(i)), array,
2129 value, effect, control);
2130 }
2131 }
2132
2133 effect = graph()->NewNode(simplified()->StoreField(context_field), generator,
2134 context, effect, control);
2135 effect = graph()->NewNode(simplified()->StoreField(continuation_field),
2136 generator, continuation, effect, control);
2137 effect = graph()->NewNode(simplified()->StoreField(input_or_debug_pos_field),
2138 generator, offset, effect, control);
2139
2140 ReplaceWithValue(node, effect, effect, control);
2141 return Changed(effect);
2142 }
2143
ReduceJSGeneratorRestoreContinuation(Node * node)2144 Reduction JSTypedLowering::ReduceJSGeneratorRestoreContinuation(Node* node) {
2145 DCHECK_EQ(IrOpcode::kJSGeneratorRestoreContinuation, node->opcode());
2146 Node* generator = NodeProperties::GetValueInput(node, 0);
2147 Node* effect = NodeProperties::GetEffectInput(node);
2148 Node* control = NodeProperties::GetControlInput(node);
2149
2150 FieldAccess continuation_field =
2151 AccessBuilder::ForJSGeneratorObjectContinuation();
2152
2153 Node* continuation = effect = graph()->NewNode(
2154 simplified()->LoadField(continuation_field), generator, effect, control);
2155 Node* executing = jsgraph()->Constant(JSGeneratorObject::kGeneratorExecuting);
2156 effect = graph()->NewNode(simplified()->StoreField(continuation_field),
2157 generator, executing, effect, control);
2158
2159 ReplaceWithValue(node, continuation, effect, control);
2160 return Changed(continuation);
2161 }
2162
ReduceJSGeneratorRestoreContext(Node * node)2163 Reduction JSTypedLowering::ReduceJSGeneratorRestoreContext(Node* node) {
2164 DCHECK_EQ(IrOpcode::kJSGeneratorRestoreContext, node->opcode());
2165
2166 const Operator* new_op =
2167 simplified()->LoadField(AccessBuilder::ForJSGeneratorObjectContext());
2168
2169 // Mutate the node in-place.
2170 DCHECK(OperatorProperties::HasContextInput(node->op()));
2171 DCHECK(!OperatorProperties::HasContextInput(new_op));
2172 node->RemoveInput(NodeProperties::FirstContextIndex(node));
2173
2174 NodeProperties::ChangeOp(node, new_op);
2175 return Changed(node);
2176 }
2177
ReduceJSGeneratorRestoreRegister(Node * node)2178 Reduction JSTypedLowering::ReduceJSGeneratorRestoreRegister(Node* node) {
2179 DCHECK_EQ(IrOpcode::kJSGeneratorRestoreRegister, node->opcode());
2180 Node* generator = NodeProperties::GetValueInput(node, 0);
2181 Node* effect = NodeProperties::GetEffectInput(node);
2182 Node* control = NodeProperties::GetControlInput(node);
2183 int index = RestoreRegisterIndexOf(node->op());
2184
2185 FieldAccess array_field =
2186 AccessBuilder::ForJSGeneratorObjectParametersAndRegisters();
2187 FieldAccess element_field = AccessBuilder::ForFixedArraySlot(index);
2188
2189 Node* array = effect = graph()->NewNode(simplified()->LoadField(array_field),
2190 generator, effect, control);
2191 Node* element = effect = graph()->NewNode(
2192 simplified()->LoadField(element_field), array, effect, control);
2193 Node* stale = jsgraph()->StaleRegisterConstant();
2194 effect = graph()->NewNode(simplified()->StoreField(element_field), array,
2195 stale, effect, control);
2196
2197 ReplaceWithValue(node, element, effect, control);
2198 return Changed(element);
2199 }
2200
ReduceJSGeneratorRestoreInputOrDebugPos(Node * node)2201 Reduction JSTypedLowering::ReduceJSGeneratorRestoreInputOrDebugPos(Node* node) {
2202 DCHECK_EQ(IrOpcode::kJSGeneratorRestoreInputOrDebugPos, node->opcode());
2203
2204 FieldAccess input_or_debug_pos_field =
2205 AccessBuilder::ForJSGeneratorObjectInputOrDebugPos();
2206 const Operator* new_op = simplified()->LoadField(input_or_debug_pos_field);
2207
2208 // Mutate the node in-place.
2209 DCHECK(OperatorProperties::HasContextInput(node->op()));
2210 DCHECK(!OperatorProperties::HasContextInput(new_op));
2211 node->RemoveInput(NodeProperties::FirstContextIndex(node));
2212
2213 NodeProperties::ChangeOp(node, new_op);
2214 return Changed(node);
2215 }
2216
ReduceObjectIsArray(Node * node)2217 Reduction JSTypedLowering::ReduceObjectIsArray(Node* node) {
2218 Node* value = NodeProperties::GetValueInput(node, 0);
2219 Type value_type = NodeProperties::GetType(value);
2220 Node* context = NodeProperties::GetContextInput(node);
2221 Node* frame_state = NodeProperties::GetFrameStateInput(node);
2222 Node* effect = NodeProperties::GetEffectInput(node);
2223 Node* control = NodeProperties::GetControlInput(node);
2224
2225 // Constant-fold based on {value} type.
2226 if (value_type.Is(Type::Array())) {
2227 Node* value = jsgraph()->TrueConstant();
2228 ReplaceWithValue(node, value);
2229 return Replace(value);
2230 } else if (!value_type.Maybe(Type::ArrayOrProxy())) {
2231 Node* value = jsgraph()->FalseConstant();
2232 ReplaceWithValue(node, value);
2233 return Replace(value);
2234 }
2235
2236 int count = 0;
2237 Node* values[5];
2238 Node* effects[5];
2239 Node* controls[4];
2240
2241 // Check if the {value} is a Smi.
2242 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value);
2243 control =
2244 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
2245
2246 // The {value} is a Smi.
2247 controls[count] = graph()->NewNode(common()->IfTrue(), control);
2248 effects[count] = effect;
2249 values[count] = jsgraph()->FalseConstant();
2250 count++;
2251
2252 control = graph()->NewNode(common()->IfFalse(), control);
2253
2254 // Load the {value}s instance type.
2255 Node* value_map = effect = graph()->NewNode(
2256 simplified()->LoadField(AccessBuilder::ForMap()), value, effect, control);
2257 Node* value_instance_type = effect = graph()->NewNode(
2258 simplified()->LoadField(AccessBuilder::ForMapInstanceType()), value_map,
2259 effect, control);
2260
2261 // Check if the {value} is a JSArray.
2262 check = graph()->NewNode(simplified()->NumberEqual(), value_instance_type,
2263 jsgraph()->Constant(JS_ARRAY_TYPE));
2264 control = graph()->NewNode(common()->Branch(), check, control);
2265
2266 // The {value} is a JSArray.
2267 controls[count] = graph()->NewNode(common()->IfTrue(), control);
2268 effects[count] = effect;
2269 values[count] = jsgraph()->TrueConstant();
2270 count++;
2271
2272 control = graph()->NewNode(common()->IfFalse(), control);
2273
2274 // Check if the {value} is a JSProxy.
2275 check = graph()->NewNode(simplified()->NumberEqual(), value_instance_type,
2276 jsgraph()->Constant(JS_PROXY_TYPE));
2277 control =
2278 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
2279
2280 // The {value} is neither a JSArray nor a JSProxy.
2281 controls[count] = graph()->NewNode(common()->IfFalse(), control);
2282 effects[count] = effect;
2283 values[count] = jsgraph()->FalseConstant();
2284 count++;
2285
2286 control = graph()->NewNode(common()->IfTrue(), control);
2287
2288 // Let the %ArrayIsArray runtime function deal with the JSProxy {value}.
2289 value = effect = control =
2290 graph()->NewNode(javascript()->CallRuntime(Runtime::kArrayIsArray), value,
2291 context, frame_state, effect, control);
2292 NodeProperties::SetType(value, Type::Boolean());
2293
2294 // Update potential {IfException} uses of {node} to point to the above
2295 // %ArrayIsArray runtime call node instead.
2296 Node* on_exception = nullptr;
2297 if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
2298 NodeProperties::ReplaceControlInput(on_exception, control);
2299 NodeProperties::ReplaceEffectInput(on_exception, effect);
2300 control = graph()->NewNode(common()->IfSuccess(), control);
2301 Revisit(on_exception);
2302 }
2303
2304 // The {value} is neither a JSArray nor a JSProxy.
2305 controls[count] = control;
2306 effects[count] = effect;
2307 values[count] = value;
2308 count++;
2309
2310 control = graph()->NewNode(common()->Merge(count), count, controls);
2311 effects[count] = control;
2312 values[count] = control;
2313 effect = graph()->NewNode(common()->EffectPhi(count), count + 1, effects);
2314 value = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, count),
2315 count + 1, values);
2316 ReplaceWithValue(node, value, effect, control);
2317 return Replace(value);
2318 }
2319
ReduceJSParseInt(Node * node)2320 Reduction JSTypedLowering::ReduceJSParseInt(Node* node) {
2321 Node* value = NodeProperties::GetValueInput(node, 0);
2322 Type value_type = NodeProperties::GetType(value);
2323 Node* radix = NodeProperties::GetValueInput(node, 1);
2324 Type radix_type = NodeProperties::GetType(radix);
2325 // We need kTenOrUndefined and kZeroOrUndefined because
2326 // the type representing {0,10} would become the range 1-10.
2327 if (value_type.Is(type_cache_->kSafeInteger) &&
2328 (radix_type.Is(type_cache_->kTenOrUndefined) ||
2329 radix_type.Is(type_cache_->kZeroOrUndefined))) {
2330 // Number.parseInt(a:safe-integer) -> a
2331 // Number.parseInt(a:safe-integer,b:#0\/undefined) -> a
2332 // Number.parseInt(a:safe-integer,b:#10\/undefined) -> a
2333 ReplaceWithValue(node, value);
2334 return Replace(value);
2335 }
2336 return NoChange();
2337 }
2338
ReduceJSResolvePromise(Node * node)2339 Reduction JSTypedLowering::ReduceJSResolvePromise(Node* node) {
2340 DCHECK_EQ(IrOpcode::kJSResolvePromise, node->opcode());
2341 Node* resolution = NodeProperties::GetValueInput(node, 1);
2342 Type resolution_type = NodeProperties::GetType(resolution);
2343 // We can strength-reduce JSResolvePromise to JSFulfillPromise
2344 // if the {resolution} is known to be a primitive, as in that
2345 // case we don't perform the implicit chaining (via "then").
2346 if (resolution_type.Is(Type::Primitive())) {
2347 // JSResolvePromise(p,v:primitive) -> JSFulfillPromise(p,v)
2348 node->RemoveInput(3); // frame state
2349 NodeProperties::ChangeOp(node, javascript()->FulfillPromise());
2350 return Changed(node);
2351 }
2352 return NoChange();
2353 }
2354
Reduce(Node * node)2355 Reduction JSTypedLowering::Reduce(Node* node) {
2356 switch (node->opcode()) {
2357 case IrOpcode::kJSEqual:
2358 return ReduceJSEqual(node);
2359 case IrOpcode::kJSStrictEqual:
2360 return ReduceJSStrictEqual(node);
2361 case IrOpcode::kJSLessThan: // fall through
2362 case IrOpcode::kJSGreaterThan: // fall through
2363 case IrOpcode::kJSLessThanOrEqual: // fall through
2364 case IrOpcode::kJSGreaterThanOrEqual:
2365 return ReduceJSComparison(node);
2366 case IrOpcode::kJSBitwiseOr:
2367 case IrOpcode::kJSBitwiseXor:
2368 case IrOpcode::kJSBitwiseAnd:
2369 return ReduceInt32Binop(node);
2370 case IrOpcode::kJSShiftLeft:
2371 case IrOpcode::kJSShiftRight:
2372 return ReduceUI32Shift(node, kSigned);
2373 case IrOpcode::kJSShiftRightLogical:
2374 return ReduceUI32Shift(node, kUnsigned);
2375 case IrOpcode::kJSAdd:
2376 return ReduceJSAdd(node);
2377 case IrOpcode::kJSSubtract:
2378 case IrOpcode::kJSMultiply:
2379 case IrOpcode::kJSDivide:
2380 case IrOpcode::kJSModulus:
2381 case IrOpcode::kJSExponentiate:
2382 return ReduceNumberBinop(node);
2383 case IrOpcode::kJSBitwiseNot:
2384 return ReduceJSBitwiseNot(node);
2385 case IrOpcode::kJSDecrement:
2386 return ReduceJSDecrement(node);
2387 case IrOpcode::kJSIncrement:
2388 return ReduceJSIncrement(node);
2389 case IrOpcode::kJSNegate:
2390 return ReduceJSNegate(node);
2391 case IrOpcode::kJSHasInPrototypeChain:
2392 return ReduceJSHasInPrototypeChain(node);
2393 case IrOpcode::kJSOrdinaryHasInstance:
2394 return ReduceJSOrdinaryHasInstance(node);
2395 case IrOpcode::kJSToLength:
2396 return ReduceJSToLength(node);
2397 case IrOpcode::kJSToName:
2398 return ReduceJSToName(node);
2399 case IrOpcode::kJSToNumber:
2400 case IrOpcode::kJSToNumberConvertBigInt:
2401 return ReduceJSToNumber(node);
2402 case IrOpcode::kJSToNumeric:
2403 return ReduceJSToNumeric(node);
2404 case IrOpcode::kJSToString:
2405 return ReduceJSToString(node);
2406 case IrOpcode::kJSToObject:
2407 return ReduceJSToObject(node);
2408 case IrOpcode::kJSLoadNamed:
2409 return ReduceJSLoadNamed(node);
2410 case IrOpcode::kJSLoadContext:
2411 return ReduceJSLoadContext(node);
2412 case IrOpcode::kJSStoreContext:
2413 return ReduceJSStoreContext(node);
2414 case IrOpcode::kJSLoadModule:
2415 return ReduceJSLoadModule(node);
2416 case IrOpcode::kJSStoreModule:
2417 return ReduceJSStoreModule(node);
2418 case IrOpcode::kJSConstructForwardVarargs:
2419 return ReduceJSConstructForwardVarargs(node);
2420 case IrOpcode::kJSConstruct:
2421 return ReduceJSConstruct(node);
2422 case IrOpcode::kJSCallForwardVarargs:
2423 return ReduceJSCallForwardVarargs(node);
2424 case IrOpcode::kJSCall:
2425 return ReduceJSCall(node);
2426 case IrOpcode::kJSForInPrepare:
2427 return ReduceJSForInPrepare(node);
2428 case IrOpcode::kJSForInNext:
2429 return ReduceJSForInNext(node);
2430 case IrOpcode::kJSHasContextExtension:
2431 return ReduceJSHasContextExtension(node);
2432 case IrOpcode::kJSLoadMessage:
2433 return ReduceJSLoadMessage(node);
2434 case IrOpcode::kJSStoreMessage:
2435 return ReduceJSStoreMessage(node);
2436 case IrOpcode::kJSGeneratorStore:
2437 return ReduceJSGeneratorStore(node);
2438 case IrOpcode::kJSGeneratorRestoreContinuation:
2439 return ReduceJSGeneratorRestoreContinuation(node);
2440 case IrOpcode::kJSGeneratorRestoreContext:
2441 return ReduceJSGeneratorRestoreContext(node);
2442 case IrOpcode::kJSGeneratorRestoreRegister:
2443 return ReduceJSGeneratorRestoreRegister(node);
2444 case IrOpcode::kJSGeneratorRestoreInputOrDebugPos:
2445 return ReduceJSGeneratorRestoreInputOrDebugPos(node);
2446 case IrOpcode::kJSObjectIsArray:
2447 return ReduceObjectIsArray(node);
2448 case IrOpcode::kJSParseInt:
2449 return ReduceJSParseInt(node);
2450 case IrOpcode::kJSResolvePromise:
2451 return ReduceJSResolvePromise(node);
2452 default:
2453 break;
2454 }
2455 return NoChange();
2456 }
2457
factory() const2458 Factory* JSTypedLowering::factory() const { return jsgraph()->factory(); }
2459
graph() const2460 Graph* JSTypedLowering::graph() const { return jsgraph()->graph(); }
2461
dependencies() const2462 CompilationDependencies* JSTypedLowering::dependencies() const {
2463 return broker()->dependencies();
2464 }
2465
isolate() const2466 Isolate* JSTypedLowering::isolate() const { return jsgraph()->isolate(); }
2467
javascript() const2468 JSOperatorBuilder* JSTypedLowering::javascript() const {
2469 return jsgraph()->javascript();
2470 }
2471
common() const2472 CommonOperatorBuilder* JSTypedLowering::common() const {
2473 return jsgraph()->common();
2474 }
2475
simplified() const2476 SimplifiedOperatorBuilder* JSTypedLowering::simplified() const {
2477 return jsgraph()->simplified();
2478 }
2479
2480 } // namespace compiler
2481 } // namespace internal
2482 } // namespace v8
2483