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