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 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 CodeTRef 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 directly callable JSFunction.
1661 if (target_type.Is(Type::CallableFunction())) {
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 // We need to check here in addition to JSCallReducer for Realms.
1726 // TODO(pthier): Consolidate all the class constructor checks.
1727 if (IsClassConstructor(shared->kind())) return NoChange();
1728
1729 // Check if we need to convert the {receiver}, but bailout if it would
1730 // require data from a foreign native context.
1731 if (is_sloppy(shared->language_mode()) && !shared->native() &&
1732 !receiver_type.Is(Type::Receiver())) {
1733 if (!function.has_value() || !function->native_context().equals(
1734 broker()->target_native_context())) {
1735 return NoChange();
1736 }
1737 Node* global_proxy =
1738 jsgraph()->Constant(function->native_context().global_proxy_object());
1739 receiver = effect =
1740 graph()->NewNode(simplified()->ConvertReceiver(convert_mode),
1741 receiver, global_proxy, effect, control);
1742 NodeProperties::ReplaceValueInput(node, receiver, 1);
1743 }
1744
1745 // Load the context from the {target}.
1746 Node* context = effect = graph()->NewNode(
1747 simplified()->LoadField(AccessBuilder::ForJSFunctionContext()), target,
1748 effect, control);
1749 NodeProperties::ReplaceContextInput(node, context);
1750
1751 // Update the effect dependency for the {node}.
1752 NodeProperties::ReplaceEffectInput(node, effect);
1753
1754 // Compute flags for the call.
1755 CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
1756 Node* new_target = jsgraph()->UndefinedConstant();
1757
1758 int formal_count =
1759 shared->internal_formal_parameter_count_without_receiver();
1760 if (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
1787 const CallInterfaceDescriptor& descriptor = callable.descriptor();
1788 auto call_descriptor = Linkage::GetStubCallDescriptor(
1789 graph()->zone(), descriptor, 1 + arity, flags);
1790 Node* stub_code = jsgraph()->HeapConstant(callable.code());
1791 node->RemoveInput(n.FeedbackVectorIndex());
1792 node->InsertInput(graph()->zone(), 0, stub_code); // Code object.
1793 node->InsertInput(graph()->zone(), 2, new_target);
1794 node->InsertInput(graph()->zone(), 3,
1795 jsgraph()->Constant(JSParameterCount(arity)));
1796 NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
1797 } else {
1798 // Patch {node} to a direct call.
1799 node->RemoveInput(n.FeedbackVectorIndex());
1800 node->InsertInput(graph()->zone(), arity + 2, new_target);
1801 node->InsertInput(graph()->zone(), arity + 3,
1802 jsgraph()->Constant(JSParameterCount(arity)));
1803 NodeProperties::ChangeOp(node,
1804 common()->Call(Linkage::GetJSCallDescriptor(
1805 graph()->zone(), false, 1 + arity,
1806 flags | CallDescriptor::kCanUseRoots)));
1807 }
1808 return Changed(node);
1809 }
1810
1811 // Check if {target} is a directly callable JSFunction.
1812 if (target_type.Is(Type::CallableFunction())) {
1813 // The node will change operators, remove the feedback vector.
1814 node->RemoveInput(n.FeedbackVectorIndex());
1815 // Compute flags for the call.
1816 CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
1817 // Patch {node} to an indirect call via the CallFunction builtin.
1818 Callable callable = CodeFactory::CallFunction(isolate(), convert_mode);
1819 node->InsertInput(graph()->zone(), 0,
1820 jsgraph()->HeapConstant(callable.code()));
1821 node->InsertInput(graph()->zone(), 2,
1822 jsgraph()->Constant(JSParameterCount(arity)));
1823 NodeProperties::ChangeOp(
1824 node, common()->Call(Linkage::GetStubCallDescriptor(
1825 graph()->zone(), callable.descriptor(), 1 + arity, flags)));
1826 return Changed(node);
1827 }
1828
1829 // Maybe we did at least learn something about the {receiver}.
1830 if (p.convert_mode() != convert_mode) {
1831 NodeProperties::ChangeOp(
1832 node,
1833 javascript()->Call(p.arity(), p.frequency(), p.feedback(), convert_mode,
1834 p.speculation_mode(), p.feedback_relation()));
1835 return Changed(node);
1836 }
1837
1838 return NoChange();
1839 }
1840
ReduceJSForInNext(Node * node)1841 Reduction JSTypedLowering::ReduceJSForInNext(Node* node) {
1842 JSForInNextNode n(node);
1843 Node* receiver = n.receiver();
1844 Node* cache_array = n.cache_array();
1845 Node* cache_type = n.cache_type();
1846 Node* index = n.index();
1847 Node* context = n.context();
1848 FrameState frame_state = n.frame_state();
1849 Effect effect = n.effect();
1850 Control control = n.control();
1851
1852 // Load the map of the {receiver}.
1853 Node* receiver_map = effect =
1854 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
1855 receiver, effect, control);
1856
1857 switch (n.Parameters().mode()) {
1858 case ForInMode::kUseEnumCacheKeys:
1859 case ForInMode::kUseEnumCacheKeysAndIndices: {
1860 // Ensure that the expected map still matches that of the {receiver}.
1861 Node* check = graph()->NewNode(simplified()->ReferenceEqual(),
1862 receiver_map, cache_type);
1863 effect =
1864 graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kWrongMap),
1865 check, effect, control);
1866
1867 // Since the change to LoadElement() below is effectful, we connect
1868 // node to all effect uses.
1869 ReplaceWithValue(node, node, node, control);
1870
1871 // Morph the {node} into a LoadElement.
1872 node->ReplaceInput(0, cache_array);
1873 node->ReplaceInput(1, index);
1874 node->ReplaceInput(2, effect);
1875 node->ReplaceInput(3, control);
1876 node->TrimInputCount(4);
1877 NodeProperties::ChangeOp(
1878 node,
1879 simplified()->LoadElement(AccessBuilder::ForFixedArrayElement()));
1880 NodeProperties::SetType(node, Type::InternalizedString());
1881 break;
1882 }
1883 case ForInMode::kGeneric: {
1884 // Load the next {key} from the {cache_array}.
1885 Node* key = effect = graph()->NewNode(
1886 simplified()->LoadElement(AccessBuilder::ForFixedArrayElement()),
1887 cache_array, index, effect, control);
1888
1889 // Check if the expected map still matches that of the {receiver}.
1890 Node* check = graph()->NewNode(simplified()->ReferenceEqual(),
1891 receiver_map, cache_type);
1892 Node* branch =
1893 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
1894
1895 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
1896 Node* etrue;
1897 Node* vtrue;
1898 {
1899 // Don't need filtering since expected map still matches that of the
1900 // {receiver}.
1901 etrue = effect;
1902 vtrue = key;
1903 }
1904
1905 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
1906 Node* efalse;
1907 Node* vfalse;
1908 {
1909 // Filter the {key} to check if it's still a valid property of the
1910 // {receiver} (does the ToName conversion implicitly).
1911 Callable const callable =
1912 Builtins::CallableFor(isolate(), Builtin::kForInFilter);
1913 auto call_descriptor = Linkage::GetStubCallDescriptor(
1914 graph()->zone(), callable.descriptor(),
1915 callable.descriptor().GetStackParameterCount(),
1916 CallDescriptor::kNeedsFrameState);
1917 vfalse = efalse = if_false =
1918 graph()->NewNode(common()->Call(call_descriptor),
1919 jsgraph()->HeapConstant(callable.code()), key,
1920 receiver, context, frame_state, effect, if_false);
1921 NodeProperties::SetType(
1922 vfalse,
1923 Type::Union(Type::String(), Type::Undefined(), graph()->zone()));
1924
1925 // Update potential {IfException} uses of {node} to point to the above
1926 // ForInFilter stub call node instead.
1927 Node* if_exception = nullptr;
1928 if (NodeProperties::IsExceptionalCall(node, &if_exception)) {
1929 if_false = graph()->NewNode(common()->IfSuccess(), vfalse);
1930 NodeProperties::ReplaceControlInput(if_exception, vfalse);
1931 NodeProperties::ReplaceEffectInput(if_exception, efalse);
1932 Revisit(if_exception);
1933 }
1934 }
1935
1936 control = graph()->NewNode(common()->Merge(2), if_true, if_false);
1937 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
1938 ReplaceWithValue(node, node, effect, control);
1939
1940 // Morph the {node} into a Phi.
1941 node->ReplaceInput(0, vtrue);
1942 node->ReplaceInput(1, vfalse);
1943 node->ReplaceInput(2, control);
1944 node->TrimInputCount(3);
1945 NodeProperties::ChangeOp(
1946 node, common()->Phi(MachineRepresentation::kTagged, 2));
1947 }
1948 }
1949
1950 return Changed(node);
1951 }
1952
ReduceJSForInPrepare(Node * node)1953 Reduction JSTypedLowering::ReduceJSForInPrepare(Node* node) {
1954 JSForInPrepareNode n(node);
1955 Node* enumerator = n.enumerator();
1956 Effect effect = n.effect();
1957 Control control = n.control();
1958 Node* cache_type = enumerator;
1959 Node* cache_array = nullptr;
1960 Node* cache_length = nullptr;
1961
1962 switch (n.Parameters().mode()) {
1963 case ForInMode::kUseEnumCacheKeys:
1964 case ForInMode::kUseEnumCacheKeysAndIndices: {
1965 // Check that the {enumerator} is a Map.
1966 effect = graph()->NewNode(
1967 simplified()->CheckMaps(CheckMapsFlag::kNone,
1968 ZoneHandleSet<Map>(factory()->meta_map())),
1969 enumerator, effect, control);
1970
1971 // Load the enum cache from the {enumerator} map.
1972 Node* descriptor_array = effect = graph()->NewNode(
1973 simplified()->LoadField(AccessBuilder::ForMapDescriptors()),
1974 enumerator, effect, control);
1975 Node* enum_cache = effect = graph()->NewNode(
1976 simplified()->LoadField(AccessBuilder::ForDescriptorArrayEnumCache()),
1977 descriptor_array, effect, control);
1978 cache_array = effect = graph()->NewNode(
1979 simplified()->LoadField(AccessBuilder::ForEnumCacheKeys()),
1980 enum_cache, effect, control);
1981
1982 // Load the enum length of the {enumerator} map.
1983 Node* bit_field3 = effect = graph()->NewNode(
1984 simplified()->LoadField(AccessBuilder::ForMapBitField3()), enumerator,
1985 effect, control);
1986 STATIC_ASSERT(Map::Bits3::EnumLengthBits::kShift == 0);
1987 cache_length = graph()->NewNode(
1988 simplified()->NumberBitwiseAnd(), bit_field3,
1989 jsgraph()->Constant(Map::Bits3::EnumLengthBits::kMask));
1990 break;
1991 }
1992 case ForInMode::kGeneric: {
1993 // Check if the {enumerator} is a Map or a FixedArray.
1994 Node* check = effect = graph()->NewNode(
1995 simplified()->CompareMaps(ZoneHandleSet<Map>(factory()->meta_map())),
1996 enumerator, effect, control);
1997 Node* branch =
1998 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
1999
2000 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
2001 Node* etrue = effect;
2002 Node* cache_array_true;
2003 Node* cache_length_true;
2004 {
2005 // Load the enum cache from the {enumerator} map.
2006 Node* descriptor_array = etrue = graph()->NewNode(
2007 simplified()->LoadField(AccessBuilder::ForMapDescriptors()),
2008 enumerator, etrue, if_true);
2009 Node* enum_cache = etrue =
2010 graph()->NewNode(simplified()->LoadField(
2011 AccessBuilder::ForDescriptorArrayEnumCache()),
2012 descriptor_array, etrue, if_true);
2013 cache_array_true = etrue = graph()->NewNode(
2014 simplified()->LoadField(AccessBuilder::ForEnumCacheKeys()),
2015 enum_cache, etrue, if_true);
2016
2017 // Load the enum length of the {enumerator} map.
2018 Node* bit_field3 = etrue = graph()->NewNode(
2019 simplified()->LoadField(AccessBuilder::ForMapBitField3()),
2020 enumerator, etrue, if_true);
2021 STATIC_ASSERT(Map::Bits3::EnumLengthBits::kShift == 0);
2022 cache_length_true = graph()->NewNode(
2023 simplified()->NumberBitwiseAnd(), bit_field3,
2024 jsgraph()->Constant(Map::Bits3::EnumLengthBits::kMask));
2025 }
2026
2027 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
2028 Node* efalse = effect;
2029 Node* cache_array_false;
2030 Node* cache_length_false;
2031 {
2032 // The {enumerator} is the FixedArray with the keys to iterate.
2033 cache_array_false = enumerator;
2034 cache_length_false = efalse = graph()->NewNode(
2035 simplified()->LoadField(AccessBuilder::ForFixedArrayLength()),
2036 cache_array_false, efalse, if_false);
2037 }
2038
2039 // Rewrite the uses of the {node}.
2040 control = graph()->NewNode(common()->Merge(2), if_true, if_false);
2041 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
2042 cache_array =
2043 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
2044 cache_array_true, cache_array_false, control);
2045 cache_length =
2046 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
2047 cache_length_true, cache_length_false, control);
2048 break;
2049 }
2050 }
2051
2052 // Update the uses of {node}.
2053 for (Edge edge : node->use_edges()) {
2054 Node* const user = edge.from();
2055 if (NodeProperties::IsEffectEdge(edge)) {
2056 edge.UpdateTo(effect);
2057 Revisit(user);
2058 } else if (NodeProperties::IsControlEdge(edge)) {
2059 edge.UpdateTo(control);
2060 Revisit(user);
2061 } else {
2062 DCHECK(NodeProperties::IsValueEdge(edge));
2063 switch (ProjectionIndexOf(user->op())) {
2064 case 0:
2065 Replace(user, cache_type);
2066 break;
2067 case 1:
2068 Replace(user, cache_array);
2069 break;
2070 case 2:
2071 Replace(user, cache_length);
2072 break;
2073 default:
2074 UNREACHABLE();
2075 }
2076 }
2077 }
2078 node->Kill();
2079 return Replace(effect);
2080 }
2081
ReduceJSLoadMessage(Node * node)2082 Reduction JSTypedLowering::ReduceJSLoadMessage(Node* node) {
2083 DCHECK_EQ(IrOpcode::kJSLoadMessage, node->opcode());
2084 ExternalReference const ref =
2085 ExternalReference::address_of_pending_message(isolate());
2086 node->ReplaceInput(0, jsgraph()->ExternalConstant(ref));
2087 NodeProperties::ChangeOp(node, simplified()->LoadMessage());
2088 return Changed(node);
2089 }
2090
ReduceJSStoreMessage(Node * node)2091 Reduction JSTypedLowering::ReduceJSStoreMessage(Node* node) {
2092 DCHECK_EQ(IrOpcode::kJSStoreMessage, node->opcode());
2093 ExternalReference const ref =
2094 ExternalReference::address_of_pending_message(isolate());
2095 Node* value = NodeProperties::GetValueInput(node, 0);
2096 node->ReplaceInput(0, jsgraph()->ExternalConstant(ref));
2097 node->ReplaceInput(1, value);
2098 NodeProperties::ChangeOp(node, simplified()->StoreMessage());
2099 return Changed(node);
2100 }
2101
ReduceJSGeneratorStore(Node * node)2102 Reduction JSTypedLowering::ReduceJSGeneratorStore(Node* node) {
2103 DCHECK_EQ(IrOpcode::kJSGeneratorStore, node->opcode());
2104 Node* generator = NodeProperties::GetValueInput(node, 0);
2105 Node* continuation = NodeProperties::GetValueInput(node, 1);
2106 Node* offset = NodeProperties::GetValueInput(node, 2);
2107 Node* context = NodeProperties::GetContextInput(node);
2108 Node* effect = NodeProperties::GetEffectInput(node);
2109 Node* control = NodeProperties::GetControlInput(node);
2110 int value_count = GeneratorStoreValueCountOf(node->op());
2111
2112 FieldAccess array_field =
2113 AccessBuilder::ForJSGeneratorObjectParametersAndRegisters();
2114 FieldAccess context_field = AccessBuilder::ForJSGeneratorObjectContext();
2115 FieldAccess continuation_field =
2116 AccessBuilder::ForJSGeneratorObjectContinuation();
2117 FieldAccess input_or_debug_pos_field =
2118 AccessBuilder::ForJSGeneratorObjectInputOrDebugPos();
2119
2120 Node* array = effect = graph()->NewNode(simplified()->LoadField(array_field),
2121 generator, effect, control);
2122
2123 for (int i = 0; i < value_count; ++i) {
2124 Node* value = NodeProperties::GetValueInput(node, 3 + i);
2125 if (value != jsgraph()->OptimizedOutConstant()) {
2126 effect = graph()->NewNode(
2127 simplified()->StoreField(AccessBuilder::ForFixedArraySlot(i)), array,
2128 value, effect, control);
2129 }
2130 }
2131
2132 effect = graph()->NewNode(simplified()->StoreField(context_field), generator,
2133 context, effect, control);
2134 effect = graph()->NewNode(simplified()->StoreField(continuation_field),
2135 generator, continuation, effect, control);
2136 effect = graph()->NewNode(simplified()->StoreField(input_or_debug_pos_field),
2137 generator, offset, effect, control);
2138
2139 ReplaceWithValue(node, effect, effect, control);
2140 return Changed(effect);
2141 }
2142
ReduceJSGeneratorRestoreContinuation(Node * node)2143 Reduction JSTypedLowering::ReduceJSGeneratorRestoreContinuation(Node* node) {
2144 DCHECK_EQ(IrOpcode::kJSGeneratorRestoreContinuation, node->opcode());
2145 Node* generator = NodeProperties::GetValueInput(node, 0);
2146 Node* effect = NodeProperties::GetEffectInput(node);
2147 Node* control = NodeProperties::GetControlInput(node);
2148
2149 FieldAccess continuation_field =
2150 AccessBuilder::ForJSGeneratorObjectContinuation();
2151
2152 Node* continuation = effect = graph()->NewNode(
2153 simplified()->LoadField(continuation_field), generator, effect, control);
2154 Node* executing = jsgraph()->Constant(JSGeneratorObject::kGeneratorExecuting);
2155 effect = graph()->NewNode(simplified()->StoreField(continuation_field),
2156 generator, executing, effect, control);
2157
2158 ReplaceWithValue(node, continuation, effect, control);
2159 return Changed(continuation);
2160 }
2161
ReduceJSGeneratorRestoreContext(Node * node)2162 Reduction JSTypedLowering::ReduceJSGeneratorRestoreContext(Node* node) {
2163 DCHECK_EQ(IrOpcode::kJSGeneratorRestoreContext, node->opcode());
2164
2165 const Operator* new_op =
2166 simplified()->LoadField(AccessBuilder::ForJSGeneratorObjectContext());
2167
2168 // Mutate the node in-place.
2169 DCHECK(OperatorProperties::HasContextInput(node->op()));
2170 DCHECK(!OperatorProperties::HasContextInput(new_op));
2171 node->RemoveInput(NodeProperties::FirstContextIndex(node));
2172
2173 NodeProperties::ChangeOp(node, new_op);
2174 return Changed(node);
2175 }
2176
ReduceJSGeneratorRestoreRegister(Node * node)2177 Reduction JSTypedLowering::ReduceJSGeneratorRestoreRegister(Node* node) {
2178 DCHECK_EQ(IrOpcode::kJSGeneratorRestoreRegister, node->opcode());
2179 Node* generator = NodeProperties::GetValueInput(node, 0);
2180 Node* effect = NodeProperties::GetEffectInput(node);
2181 Node* control = NodeProperties::GetControlInput(node);
2182 int index = RestoreRegisterIndexOf(node->op());
2183
2184 FieldAccess array_field =
2185 AccessBuilder::ForJSGeneratorObjectParametersAndRegisters();
2186 FieldAccess element_field = AccessBuilder::ForFixedArraySlot(index);
2187
2188 Node* array = effect = graph()->NewNode(simplified()->LoadField(array_field),
2189 generator, effect, control);
2190 Node* element = effect = graph()->NewNode(
2191 simplified()->LoadField(element_field), array, effect, control);
2192 Node* stale = jsgraph()->StaleRegisterConstant();
2193 effect = graph()->NewNode(simplified()->StoreField(element_field), array,
2194 stale, effect, control);
2195
2196 ReplaceWithValue(node, element, effect, control);
2197 return Changed(element);
2198 }
2199
ReduceJSGeneratorRestoreInputOrDebugPos(Node * node)2200 Reduction JSTypedLowering::ReduceJSGeneratorRestoreInputOrDebugPos(Node* node) {
2201 DCHECK_EQ(IrOpcode::kJSGeneratorRestoreInputOrDebugPos, node->opcode());
2202
2203 FieldAccess input_or_debug_pos_field =
2204 AccessBuilder::ForJSGeneratorObjectInputOrDebugPos();
2205 const Operator* new_op = simplified()->LoadField(input_or_debug_pos_field);
2206
2207 // Mutate the node in-place.
2208 DCHECK(OperatorProperties::HasContextInput(node->op()));
2209 DCHECK(!OperatorProperties::HasContextInput(new_op));
2210 node->RemoveInput(NodeProperties::FirstContextIndex(node));
2211
2212 NodeProperties::ChangeOp(node, new_op);
2213 return Changed(node);
2214 }
2215
ReduceObjectIsArray(Node * node)2216 Reduction JSTypedLowering::ReduceObjectIsArray(Node* node) {
2217 Node* value = NodeProperties::GetValueInput(node, 0);
2218 Type value_type = NodeProperties::GetType(value);
2219 Node* context = NodeProperties::GetContextInput(node);
2220 Node* frame_state = NodeProperties::GetFrameStateInput(node);
2221 Node* effect = NodeProperties::GetEffectInput(node);
2222 Node* control = NodeProperties::GetControlInput(node);
2223
2224 // Constant-fold based on {value} type.
2225 if (value_type.Is(Type::Array())) {
2226 value = jsgraph()->TrueConstant();
2227 ReplaceWithValue(node, value);
2228 return Replace(value);
2229 } else if (!value_type.Maybe(Type::ArrayOrProxy())) {
2230 value = jsgraph()->FalseConstant();
2231 ReplaceWithValue(node, value);
2232 return Replace(value);
2233 }
2234
2235 int count = 0;
2236 Node* values[5];
2237 Node* effects[5];
2238 Node* controls[4];
2239
2240 // Check if the {value} is a Smi.
2241 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value);
2242 control =
2243 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
2244
2245 // The {value} is a Smi.
2246 controls[count] = graph()->NewNode(common()->IfTrue(), control);
2247 effects[count] = effect;
2248 values[count] = jsgraph()->FalseConstant();
2249 count++;
2250
2251 control = graph()->NewNode(common()->IfFalse(), control);
2252
2253 // Load the {value}s instance type.
2254 Node* value_map = effect = graph()->NewNode(
2255 simplified()->LoadField(AccessBuilder::ForMap()), value, effect, control);
2256 Node* value_instance_type = effect = graph()->NewNode(
2257 simplified()->LoadField(AccessBuilder::ForMapInstanceType()), value_map,
2258 effect, control);
2259
2260 // Check if the {value} is a JSArray.
2261 check = graph()->NewNode(simplified()->NumberEqual(), value_instance_type,
2262 jsgraph()->Constant(JS_ARRAY_TYPE));
2263 control = graph()->NewNode(common()->Branch(), check, control);
2264
2265 // The {value} is a JSArray.
2266 controls[count] = graph()->NewNode(common()->IfTrue(), control);
2267 effects[count] = effect;
2268 values[count] = jsgraph()->TrueConstant();
2269 count++;
2270
2271 control = graph()->NewNode(common()->IfFalse(), control);
2272
2273 // Check if the {value} is a JSProxy.
2274 check = graph()->NewNode(simplified()->NumberEqual(), value_instance_type,
2275 jsgraph()->Constant(JS_PROXY_TYPE));
2276 control =
2277 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
2278
2279 // The {value} is neither a JSArray nor a JSProxy.
2280 controls[count] = graph()->NewNode(common()->IfFalse(), control);
2281 effects[count] = effect;
2282 values[count] = jsgraph()->FalseConstant();
2283 count++;
2284
2285 control = graph()->NewNode(common()->IfTrue(), control);
2286
2287 // Let the %ArrayIsArray runtime function deal with the JSProxy {value}.
2288 value = effect = control =
2289 graph()->NewNode(javascript()->CallRuntime(Runtime::kArrayIsArray), value,
2290 context, frame_state, effect, control);
2291 NodeProperties::SetType(value, Type::Boolean());
2292
2293 // Update potential {IfException} uses of {node} to point to the above
2294 // %ArrayIsArray runtime call node instead.
2295 Node* on_exception = nullptr;
2296 if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
2297 NodeProperties::ReplaceControlInput(on_exception, control);
2298 NodeProperties::ReplaceEffectInput(on_exception, effect);
2299 control = graph()->NewNode(common()->IfSuccess(), control);
2300 Revisit(on_exception);
2301 }
2302
2303 // The {value} is neither a JSArray nor a JSProxy.
2304 controls[count] = control;
2305 effects[count] = effect;
2306 values[count] = value;
2307 count++;
2308
2309 control = graph()->NewNode(common()->Merge(count), count, controls);
2310 effects[count] = control;
2311 values[count] = control;
2312 effect = graph()->NewNode(common()->EffectPhi(count), count + 1, effects);
2313 value = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, count),
2314 count + 1, values);
2315 ReplaceWithValue(node, value, effect, control);
2316 return Replace(value);
2317 }
2318
ReduceJSParseInt(Node * node)2319 Reduction JSTypedLowering::ReduceJSParseInt(Node* node) {
2320 Node* value = NodeProperties::GetValueInput(node, 0);
2321 Type value_type = NodeProperties::GetType(value);
2322 Node* radix = NodeProperties::GetValueInput(node, 1);
2323 Type radix_type = NodeProperties::GetType(radix);
2324 // We need kTenOrUndefined and kZeroOrUndefined because
2325 // the type representing {0,10} would become the range 1-10.
2326 if (value_type.Is(type_cache_->kSafeInteger) &&
2327 (radix_type.Is(type_cache_->kTenOrUndefined) ||
2328 radix_type.Is(type_cache_->kZeroOrUndefined))) {
2329 // Number.parseInt(a:safe-integer) -> a
2330 // Number.parseInt(a:safe-integer,b:#0\/undefined) -> a
2331 // Number.parseInt(a:safe-integer,b:#10\/undefined) -> a
2332 ReplaceWithValue(node, value);
2333 return Replace(value);
2334 }
2335 return NoChange();
2336 }
2337
ReduceJSResolvePromise(Node * node)2338 Reduction JSTypedLowering::ReduceJSResolvePromise(Node* node) {
2339 DCHECK_EQ(IrOpcode::kJSResolvePromise, node->opcode());
2340 Node* resolution = NodeProperties::GetValueInput(node, 1);
2341 Type resolution_type = NodeProperties::GetType(resolution);
2342 // We can strength-reduce JSResolvePromise to JSFulfillPromise
2343 // if the {resolution} is known to be a primitive, as in that
2344 // case we don't perform the implicit chaining (via "then").
2345 if (resolution_type.Is(Type::Primitive())) {
2346 // JSResolvePromise(p,v:primitive) -> JSFulfillPromise(p,v)
2347 node->RemoveInput(3); // frame state
2348 NodeProperties::ChangeOp(node, javascript()->FulfillPromise());
2349 return Changed(node);
2350 }
2351 return NoChange();
2352 }
2353
Reduce(Node * node)2354 Reduction JSTypedLowering::Reduce(Node* node) {
2355 switch (node->opcode()) {
2356 case IrOpcode::kJSEqual:
2357 return ReduceJSEqual(node);
2358 case IrOpcode::kJSStrictEqual:
2359 return ReduceJSStrictEqual(node);
2360 case IrOpcode::kJSLessThan: // fall through
2361 case IrOpcode::kJSGreaterThan: // fall through
2362 case IrOpcode::kJSLessThanOrEqual: // fall through
2363 case IrOpcode::kJSGreaterThanOrEqual:
2364 return ReduceJSComparison(node);
2365 case IrOpcode::kJSBitwiseOr:
2366 case IrOpcode::kJSBitwiseXor:
2367 case IrOpcode::kJSBitwiseAnd:
2368 return ReduceInt32Binop(node);
2369 case IrOpcode::kJSShiftLeft:
2370 case IrOpcode::kJSShiftRight:
2371 return ReduceUI32Shift(node, kSigned);
2372 case IrOpcode::kJSShiftRightLogical:
2373 return ReduceUI32Shift(node, kUnsigned);
2374 case IrOpcode::kJSAdd:
2375 return ReduceJSAdd(node);
2376 case IrOpcode::kJSSubtract:
2377 case IrOpcode::kJSMultiply:
2378 case IrOpcode::kJSDivide:
2379 case IrOpcode::kJSModulus:
2380 case IrOpcode::kJSExponentiate:
2381 return ReduceNumberBinop(node);
2382 case IrOpcode::kJSBitwiseNot:
2383 return ReduceJSBitwiseNot(node);
2384 case IrOpcode::kJSDecrement:
2385 return ReduceJSDecrement(node);
2386 case IrOpcode::kJSIncrement:
2387 return ReduceJSIncrement(node);
2388 case IrOpcode::kJSNegate:
2389 return ReduceJSNegate(node);
2390 case IrOpcode::kJSHasInPrototypeChain:
2391 return ReduceJSHasInPrototypeChain(node);
2392 case IrOpcode::kJSOrdinaryHasInstance:
2393 return ReduceJSOrdinaryHasInstance(node);
2394 case IrOpcode::kJSToLength:
2395 return ReduceJSToLength(node);
2396 case IrOpcode::kJSToName:
2397 return ReduceJSToName(node);
2398 case IrOpcode::kJSToNumber:
2399 case IrOpcode::kJSToNumberConvertBigInt:
2400 return ReduceJSToNumber(node);
2401 case IrOpcode::kJSToNumeric:
2402 return ReduceJSToNumeric(node);
2403 case IrOpcode::kJSToString:
2404 return ReduceJSToString(node);
2405 case IrOpcode::kJSToObject:
2406 return ReduceJSToObject(node);
2407 case IrOpcode::kJSLoadNamed:
2408 return ReduceJSLoadNamed(node);
2409 case IrOpcode::kJSLoadContext:
2410 return ReduceJSLoadContext(node);
2411 case IrOpcode::kJSStoreContext:
2412 return ReduceJSStoreContext(node);
2413 case IrOpcode::kJSLoadModule:
2414 return ReduceJSLoadModule(node);
2415 case IrOpcode::kJSStoreModule:
2416 return ReduceJSStoreModule(node);
2417 case IrOpcode::kJSConstructForwardVarargs:
2418 return ReduceJSConstructForwardVarargs(node);
2419 case IrOpcode::kJSConstruct:
2420 return ReduceJSConstruct(node);
2421 case IrOpcode::kJSCallForwardVarargs:
2422 return ReduceJSCallForwardVarargs(node);
2423 case IrOpcode::kJSCall:
2424 return ReduceJSCall(node);
2425 case IrOpcode::kJSForInPrepare:
2426 return ReduceJSForInPrepare(node);
2427 case IrOpcode::kJSForInNext:
2428 return ReduceJSForInNext(node);
2429 case IrOpcode::kJSHasContextExtension:
2430 return ReduceJSHasContextExtension(node);
2431 case IrOpcode::kJSLoadMessage:
2432 return ReduceJSLoadMessage(node);
2433 case IrOpcode::kJSStoreMessage:
2434 return ReduceJSStoreMessage(node);
2435 case IrOpcode::kJSGeneratorStore:
2436 return ReduceJSGeneratorStore(node);
2437 case IrOpcode::kJSGeneratorRestoreContinuation:
2438 return ReduceJSGeneratorRestoreContinuation(node);
2439 case IrOpcode::kJSGeneratorRestoreContext:
2440 return ReduceJSGeneratorRestoreContext(node);
2441 case IrOpcode::kJSGeneratorRestoreRegister:
2442 return ReduceJSGeneratorRestoreRegister(node);
2443 case IrOpcode::kJSGeneratorRestoreInputOrDebugPos:
2444 return ReduceJSGeneratorRestoreInputOrDebugPos(node);
2445 case IrOpcode::kJSObjectIsArray:
2446 return ReduceObjectIsArray(node);
2447 case IrOpcode::kJSParseInt:
2448 return ReduceJSParseInt(node);
2449 case IrOpcode::kJSResolvePromise:
2450 return ReduceJSResolvePromise(node);
2451 default:
2452 break;
2453 }
2454 return NoChange();
2455 }
2456
factory() const2457 Factory* JSTypedLowering::factory() const { return jsgraph()->factory(); }
2458
graph() const2459 Graph* JSTypedLowering::graph() const { return jsgraph()->graph(); }
2460
dependencies() const2461 CompilationDependencies* JSTypedLowering::dependencies() const {
2462 return broker()->dependencies();
2463 }
2464
isolate() const2465 Isolate* JSTypedLowering::isolate() const { return jsgraph()->isolate(); }
2466
javascript() const2467 JSOperatorBuilder* JSTypedLowering::javascript() const {
2468 return jsgraph()->javascript();
2469 }
2470
common() const2471 CommonOperatorBuilder* JSTypedLowering::common() const {
2472 return jsgraph()->common();
2473 }
2474
simplified() const2475 SimplifiedOperatorBuilder* JSTypedLowering::simplified() const {
2476 return jsgraph()->simplified();
2477 }
2478
2479 } // namespace compiler
2480 } // namespace internal
2481 } // namespace v8
2482