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.
5 #include "src/compiler/access-builder.h"
6 #include "src/compiler/js-graph.h"
7 #include "src/compiler/js-typed-lowering.h"
8 #include "src/compiler/node-matchers.h"
9 #include "src/compiler/node-properties.h"
10 #include "src/compiler/operator-properties.h"
11 #include "src/types.h"
17 // TODO(turbofan): js-typed-lowering improvements possible
18 // - immediately put in type bounds for all new nodes
19 // - relax effects from generic but not-side-effecting operations
22 // Relax the effects of {node} by immediately replacing effect and control uses
23 // of {node} with the effect and control input to {node}.
24 // TODO(turbofan): replace the effect input to {node} with {graph->start()}.
25 // TODO(titzer): move into a GraphEditor?
26 static void RelaxEffectsAndControls(Node* node) {
27 NodeProperties::ReplaceWithValue(node, node, NULL);
31 // Relax the control uses of {node} by immediately replacing them with the
32 // control input to {node}.
33 // TODO(titzer): move into a GraphEditor?
34 static void RelaxControls(Node* node) {
35 NodeProperties::ReplaceWithValue(node, node, node);
39 JSTypedLowering::JSTypedLowering(JSGraph* jsgraph, Zone* zone)
40 : jsgraph_(jsgraph), simplified_(graph()->zone()), conversions_(zone) {
41 zero_range_ = Type::Range(0.0, 0.0, graph()->zone());
42 one_range_ = Type::Range(1.0, 1.0, graph()->zone());
43 zero_thirtyone_range_ = Type::Range(0.0, 31.0, graph()->zone());
44 for (size_t k = 0; k < arraysize(shifted_int32_ranges_); ++k) {
45 double min = kMinInt / (1 << k);
46 double max = kMaxInt / (1 << k);
47 shifted_int32_ranges_[k] = Type::Range(min, max, graph()->zone());
52 Reduction JSTypedLowering::ReplaceEagerly(Node* old, Node* node) {
53 NodeProperties::ReplaceWithValue(old, node, node);
58 // A helper class to simplify the process of reducing a single binop node with a
59 // JSOperator. This class manages the rewriting of context, control, and effect
60 // dependencies during lowering of a binop and contains numerous helper
61 // functions for matching the types of inputs to an operation.
62 class JSBinopReduction FINAL {
64 JSBinopReduction(JSTypedLowering* lowering, Node* node)
65 : lowering_(lowering), node_(node) {}
67 void ConvertPrimitiveInputsToNumber() {
68 node_->ReplaceInput(0, ConvertPrimitiveToNumber(left()));
69 node_->ReplaceInput(1, ConvertPrimitiveToNumber(right()));
72 void ConvertInputsToNumber(Node* frame_state) {
73 // To convert the inputs to numbers, we have to provide frame states
74 // for lazy bailouts in the ToNumber conversions.
75 // We use a little hack here: we take the frame state before the binary
76 // operation and use it to construct the frame states for the conversion
77 // so that after the deoptimization, the binary operation IC gets
78 // already converted values from full code. This way we are sure that we
79 // will not re-do any of the side effects.
82 left_type()->Is(Type::PlainPrimitive())
83 ? ConvertPrimitiveToNumber(left())
84 : ConvertToNumber(left(),
85 CreateFrameStateForLeftInput(frame_state));
88 right_type()->Is(Type::PlainPrimitive())
89 ? ConvertPrimitiveToNumber(right())
90 : ConvertToNumber(right(), CreateFrameStateForRightInput(
91 frame_state, left_input));
93 node_->ReplaceInput(0, left_input);
94 node_->ReplaceInput(1, right_input);
97 void ConvertInputsToUI32(Signedness left_signedness,
98 Signedness right_signedness) {
99 node_->ReplaceInput(0, ConvertToUI32(left(), left_signedness));
100 node_->ReplaceInput(1, ConvertToUI32(right(), right_signedness));
103 void ConvertInputsToString() {
104 node_->ReplaceInput(0, ConvertToString(left()));
105 node_->ReplaceInput(1, ConvertToString(right()));
108 // Convert inputs for bitwise shift operation (ES5 spec 11.7).
109 void ConvertInputsForShift(Signedness left_signedness) {
111 0, ConvertToUI32(ConvertPrimitiveToNumber(left()), left_signedness));
112 Node* rnum = ConvertToUI32(ConvertPrimitiveToNumber(right()), kUnsigned);
113 Type* rnum_type = NodeProperties::GetBounds(rnum).upper;
114 if (!rnum_type->Is(lowering_->zero_thirtyone_range_)) {
115 rnum = graph()->NewNode(machine()->Word32And(), rnum,
116 jsgraph()->Int32Constant(0x1F));
118 node_->ReplaceInput(1, rnum);
124 node_->ReplaceInput(0, r);
125 node_->ReplaceInput(1, l);
128 // Remove all effect and control inputs and outputs to this node and change
129 // to the pure operator {op}, possibly inserting a boolean inversion.
130 Reduction ChangeToPureOperator(const Operator* op, bool invert = false,
131 Type* type = Type::Any()) {
132 DCHECK_EQ(0, op->EffectInputCount());
133 DCHECK_EQ(false, OperatorProperties::HasContextInput(op));
134 DCHECK_EQ(0, op->ControlInputCount());
135 DCHECK_EQ(2, op->ValueInputCount());
137 // Remove the effects from the node, and update its effect/control usages.
138 if (node_->op()->EffectInputCount() > 0) {
139 RelaxEffectsAndControls(node_);
141 // Remove the inputs corresponding to context, effect, and control.
142 NodeProperties::RemoveNonValueInputs(node_);
143 // Finally, update the operator to the new one.
146 // TODO(jarin): Replace the explicit typing hack with a call to some method
147 // that encapsulates changing the operator and re-typing.
148 Bounds const bounds = NodeProperties::GetBounds(node_);
149 NodeProperties::SetBounds(node_, Bounds::NarrowUpper(bounds, type, zone()));
152 // Insert an boolean not to invert the value.
153 Node* value = graph()->NewNode(simplified()->BooleanNot(), node_);
154 node_->ReplaceUses(value);
155 // Note: ReplaceUses() smashes all uses, so smash it back here.
156 value->ReplaceInput(0, node_);
157 return lowering_->Replace(value);
159 return lowering_->Changed(node_);
162 Reduction ChangeToPureOperator(const Operator* op, Type* type) {
163 return ChangeToPureOperator(op, false, type);
166 bool OneInputIs(Type* t) { return left_type()->Is(t) || right_type()->Is(t); }
168 bool BothInputsAre(Type* t) {
169 return left_type()->Is(t) && right_type()->Is(t);
172 bool OneInputCannotBe(Type* t) {
173 return !left_type()->Maybe(t) || !right_type()->Maybe(t);
176 bool NeitherInputCanBe(Type* t) {
177 return !left_type()->Maybe(t) && !right_type()->Maybe(t);
180 Node* effect() { return NodeProperties::GetEffectInput(node_); }
181 Node* control() { return NodeProperties::GetControlInput(node_); }
182 Node* context() { return NodeProperties::GetContextInput(node_); }
183 Node* left() { return NodeProperties::GetValueInput(node_, 0); }
184 Node* right() { return NodeProperties::GetValueInput(node_, 1); }
186 return NodeProperties::GetBounds(node_->InputAt(0)).upper;
189 return NodeProperties::GetBounds(node_->InputAt(1)).upper;
192 SimplifiedOperatorBuilder* simplified() { return lowering_->simplified(); }
193 Graph* graph() const { return lowering_->graph(); }
194 JSGraph* jsgraph() { return lowering_->jsgraph(); }
195 JSOperatorBuilder* javascript() { return lowering_->javascript(); }
196 MachineOperatorBuilder* machine() { return lowering_->machine(); }
197 Zone* zone() const { return graph()->zone(); }
200 JSTypedLowering* lowering_; // The containing lowering instance.
201 Node* node_; // The original node.
203 Node* ConvertToString(Node* node) {
204 // Avoid introducing too many eager ToString() operations.
205 Reduction reduced = lowering_->ReduceJSToStringInput(node);
206 if (reduced.Changed()) return reduced.replacement();
207 Node* n = graph()->NewNode(javascript()->ToString(), node, context(),
208 effect(), control());
213 Node* CreateFrameStateForLeftInput(Node* frame_state) {
214 if (!FLAG_turbo_deoptimization) return nullptr;
216 FrameStateCallInfo state_info =
217 OpParameter<FrameStateCallInfo>(frame_state);
218 // If the frame state is already the right one, just return it.
219 if (state_info.state_combine().kind() == OutputFrameStateCombine::kPokeAt &&
220 state_info.state_combine().GetOffsetToPokeAt() == 1) {
224 // Here, we smash the result of the conversion into the slot just below
225 // the stack top. This is the slot that full code uses to store the
227 const Operator* op = jsgraph()->common()->FrameState(
228 state_info.type(), state_info.bailout_id(),
229 OutputFrameStateCombine::PokeAt(1));
231 return graph()->NewNode(op, frame_state->InputAt(0),
232 frame_state->InputAt(1), frame_state->InputAt(2),
233 frame_state->InputAt(3), frame_state->InputAt(4));
236 Node* CreateFrameStateForRightInput(Node* frame_state, Node* converted_left) {
237 if (!FLAG_turbo_deoptimization) return nullptr;
239 FrameStateCallInfo state_info =
240 OpParameter<FrameStateCallInfo>(frame_state);
242 if (state_info.bailout_id() == BailoutId::None()) {
243 // Dummy frame state => just leave it as is.
247 // Create a frame state that stores the result of the operation to the
248 // top of the stack (i.e., the slot used for the right operand).
249 const Operator* op = jsgraph()->common()->FrameState(
250 state_info.type(), state_info.bailout_id(),
251 OutputFrameStateCombine::PokeAt(0));
253 // Change the left operand {converted_left} on the expression stack.
254 Node* stack = frame_state->InputAt(2);
255 DCHECK_EQ(stack->opcode(), IrOpcode::kStateValues);
256 DCHECK_GE(stack->InputCount(), 2);
258 // TODO(jarin) Allocate in a local zone or a reusable buffer.
259 NodeVector new_values(stack->InputCount(), zone());
260 for (int i = 0; i < stack->InputCount(); i++) {
261 if (i == stack->InputCount() - 2) {
262 new_values[i] = converted_left;
264 new_values[i] = stack->InputAt(i);
268 graph()->NewNode(stack->op(), stack->InputCount(), &new_values.front());
270 return graph()->NewNode(op, frame_state->InputAt(0),
271 frame_state->InputAt(1), new_stack,
272 frame_state->InputAt(3), frame_state->InputAt(4));
275 Node* ConvertPrimitiveToNumber(Node* node) {
276 return lowering_->ConvertPrimitiveToNumber(node);
279 Node* ConvertToNumber(Node* node, Node* frame_state) {
280 if (NodeProperties::GetBounds(node).upper->Is(Type::PlainPrimitive())) {
281 return ConvertPrimitiveToNumber(node);
282 } else if (!FLAG_turbo_deoptimization) {
283 // We cannot use ConvertToPrimitiveNumber here because we need context
284 // for converting general values.
285 Node* const n = graph()->NewNode(javascript()->ToNumber(), node,
286 context(), effect(), control());
291 graph()->NewNode(javascript()->ToNumber(), node, context(),
292 frame_state, effect(), control());
298 Node* ConvertToUI32(Node* node, Signedness signedness) {
299 // Avoid introducing too many eager NumberToXXnt32() operations.
300 Type* type = NodeProperties::GetBounds(node).upper;
301 if (signedness == kSigned) {
302 if (!type->Is(Type::Signed32())) {
303 node = graph()->NewNode(simplified()->NumberToInt32(), node);
306 DCHECK_EQ(kUnsigned, signedness);
307 if (!type->Is(Type::Unsigned32())) {
308 node = graph()->NewNode(simplified()->NumberToUint32(), node);
314 void update_effect(Node* effect) {
315 NodeProperties::ReplaceEffectInput(node_, effect);
320 Reduction JSTypedLowering::ReduceJSAdd(Node* node) {
321 JSBinopReduction r(this, node);
322 if (r.BothInputsAre(Type::Number())) {
323 // JSAdd(x:number, y:number) => NumberAdd(x, y)
324 return r.ChangeToPureOperator(simplified()->NumberAdd(), Type::Number());
326 if (r.NeitherInputCanBe(Type::StringOrReceiver())) {
327 // JSAdd(x:-string, y:-string) => NumberAdd(ToNumber(x), ToNumber(y))
328 Node* frame_state = FLAG_turbo_deoptimization
329 ? NodeProperties::GetFrameStateInput(node, 1)
331 r.ConvertInputsToNumber(frame_state);
332 return r.ChangeToPureOperator(simplified()->NumberAdd(), Type::Number());
335 // TODO(turbofan): Lowering of StringAdd is disabled for now because:
336 // a) The inserted ToString operation screws up valueOf vs. toString order.
337 // b) Deoptimization at ToString doesn't have corresponding bailout id.
338 // c) Our current StringAddStub is actually non-pure and requires context.
339 if (r.OneInputIs(Type::String())) {
340 // JSAdd(x:string, y:string) => StringAdd(x, y)
341 // JSAdd(x:string, y) => StringAdd(x, ToString(y))
342 // JSAdd(x, y:string) => StringAdd(ToString(x), y)
343 r.ConvertInputsToString();
344 return r.ChangeToPureOperator(simplified()->StringAdd());
351 Reduction JSTypedLowering::ReduceNumberBinop(Node* node,
352 const Operator* numberOp) {
353 JSBinopReduction r(this, node);
354 Node* frame_state = FLAG_turbo_deoptimization
355 ? NodeProperties::GetFrameStateInput(node, 1)
357 r.ConvertInputsToNumber(frame_state);
358 return r.ChangeToPureOperator(numberOp, Type::Number());
362 Reduction JSTypedLowering::ReduceInt32Binop(Node* node, const Operator* intOp) {
363 JSBinopReduction r(this, node);
364 Node* frame_state = FLAG_turbo_deoptimization
365 ? NodeProperties::GetFrameStateInput(node, 1)
367 r.ConvertInputsToNumber(frame_state);
368 r.ConvertInputsToUI32(kSigned, kSigned);
369 return r.ChangeToPureOperator(intOp, Type::Integral32());
373 Reduction JSTypedLowering::ReduceUI32Shift(Node* node,
374 Signedness left_signedness,
375 const Operator* shift_op) {
376 JSBinopReduction r(this, node);
377 if (r.BothInputsAre(Type::Primitive())) {
378 r.ConvertInputsForShift(left_signedness);
379 return r.ChangeToPureOperator(shift_op, Type::Integral32());
385 Reduction JSTypedLowering::ReduceJSComparison(Node* node) {
386 JSBinopReduction r(this, node);
387 if (r.BothInputsAre(Type::String())) {
388 // If both inputs are definitely strings, perform a string comparison.
389 const Operator* stringOp;
390 switch (node->opcode()) {
391 case IrOpcode::kJSLessThan:
392 stringOp = simplified()->StringLessThan();
394 case IrOpcode::kJSGreaterThan:
395 stringOp = simplified()->StringLessThan();
396 r.SwapInputs(); // a > b => b < a
398 case IrOpcode::kJSLessThanOrEqual:
399 stringOp = simplified()->StringLessThanOrEqual();
401 case IrOpcode::kJSGreaterThanOrEqual:
402 stringOp = simplified()->StringLessThanOrEqual();
403 r.SwapInputs(); // a >= b => b <= a
408 return r.ChangeToPureOperator(stringOp);
411 // TODO(turbofan): General ToNumber disabled for now because:
412 // a) The inserted ToNumber operation screws up observability of valueOf.
413 // b) Deoptimization at ToNumber doesn't have corresponding bailout id.
414 Type* maybe_string = Type::Union(Type::String(), Type::Receiver(), zone());
415 if (r.OneInputCannotBe(maybe_string)) {
416 // If one input cannot be a string, then emit a number comparison.
420 if (r.BothInputsAre(Type::PlainPrimitive()) &&
421 r.OneInputCannotBe(Type::StringOrReceiver())) {
422 const Operator* less_than;
423 const Operator* less_than_or_equal;
424 if (r.BothInputsAre(Type::Unsigned32())) {
425 less_than = machine()->Uint32LessThan();
426 less_than_or_equal = machine()->Uint32LessThanOrEqual();
427 } else if (r.BothInputsAre(Type::Signed32())) {
428 less_than = machine()->Int32LessThan();
429 less_than_or_equal = machine()->Int32LessThanOrEqual();
431 // TODO(turbofan): mixed signed/unsigned int32 comparisons.
432 r.ConvertPrimitiveInputsToNumber();
433 less_than = simplified()->NumberLessThan();
434 less_than_or_equal = simplified()->NumberLessThanOrEqual();
436 const Operator* comparison;
437 switch (node->opcode()) {
438 case IrOpcode::kJSLessThan:
439 comparison = less_than;
441 case IrOpcode::kJSGreaterThan:
442 comparison = less_than;
443 r.SwapInputs(); // a > b => b < a
445 case IrOpcode::kJSLessThanOrEqual:
446 comparison = less_than_or_equal;
448 case IrOpcode::kJSGreaterThanOrEqual:
449 comparison = less_than_or_equal;
450 r.SwapInputs(); // a >= b => b <= a
455 return r.ChangeToPureOperator(comparison);
457 // TODO(turbofan): relax/remove effects of this operator in other cases.
458 return NoChange(); // Keep a generic comparison.
462 Reduction JSTypedLowering::ReduceJSEqual(Node* node, bool invert) {
463 JSBinopReduction r(this, node);
465 if (r.BothInputsAre(Type::Number())) {
466 return r.ChangeToPureOperator(simplified()->NumberEqual(), invert);
468 if (r.BothInputsAre(Type::String())) {
469 return r.ChangeToPureOperator(simplified()->StringEqual(), invert);
471 if (r.BothInputsAre(Type::Receiver())) {
472 return r.ChangeToPureOperator(
473 simplified()->ReferenceEqual(Type::Receiver()), invert);
475 // TODO(turbofan): js-typed-lowering of Equal(undefined)
476 // TODO(turbofan): js-typed-lowering of Equal(null)
477 // TODO(turbofan): js-typed-lowering of Equal(boolean)
482 Reduction JSTypedLowering::ReduceJSStrictEqual(Node* node, bool invert) {
483 JSBinopReduction r(this, node);
484 if (r.left() == r.right()) {
485 // x === x is always true if x != NaN
486 if (!r.left_type()->Maybe(Type::NaN())) {
487 return ReplaceEagerly(node, jsgraph()->BooleanConstant(!invert));
490 if (r.OneInputCannotBe(Type::NumberOrString())) {
491 // For values with canonical representation (i.e. not string nor number) an
492 // empty type intersection means the values cannot be strictly equal.
493 if (!r.left_type()->Maybe(r.right_type())) {
494 return ReplaceEagerly(node, jsgraph()->BooleanConstant(invert));
497 if (r.OneInputIs(Type::Undefined())) {
498 return r.ChangeToPureOperator(
499 simplified()->ReferenceEqual(Type::Undefined()), invert);
501 if (r.OneInputIs(Type::Null())) {
502 return r.ChangeToPureOperator(simplified()->ReferenceEqual(Type::Null()),
505 if (r.OneInputIs(Type::Boolean())) {
506 return r.ChangeToPureOperator(simplified()->ReferenceEqual(Type::Boolean()),
509 if (r.OneInputIs(Type::Object())) {
510 return r.ChangeToPureOperator(simplified()->ReferenceEqual(Type::Object()),
513 if (r.OneInputIs(Type::Receiver())) {
514 return r.ChangeToPureOperator(
515 simplified()->ReferenceEqual(Type::Receiver()), invert);
517 if (r.BothInputsAre(Type::String())) {
518 return r.ChangeToPureOperator(simplified()->StringEqual(), invert);
520 if (r.BothInputsAre(Type::Number())) {
521 return r.ChangeToPureOperator(simplified()->NumberEqual(), invert);
523 // TODO(turbofan): js-typed-lowering of StrictEqual(mixed types)
528 Reduction JSTypedLowering::ReduceJSUnaryNot(Node* node) {
529 Node* const input = node->InputAt(0);
530 Type* const input_type = NodeProperties::GetBounds(input).upper;
531 if (input_type->Is(Type::Boolean())) {
532 // JSUnaryNot(x:boolean) => BooleanNot(x)
533 node->set_op(simplified()->BooleanNot());
534 node->TrimInputCount(1);
535 return Changed(node);
536 } else if (input_type->Is(Type::OrderedNumber())) {
537 // JSUnaryNot(x:number) => NumberEqual(x,#0)
538 node->set_op(simplified()->NumberEqual());
539 node->ReplaceInput(1, jsgraph()->ZeroConstant());
540 node->TrimInputCount(2);
541 return Changed(node);
542 } else if (input_type->Is(Type::String())) {
543 // JSUnaryNot(x:string) => NumberEqual(x.length,#0)
544 FieldAccess const access = AccessBuilder::ForStringLength();
545 // It is safe for the load to be effect-free (i.e. not linked into effect
546 // chain) because we assume String::length to be immutable.
547 Node* length = graph()->NewNode(simplified()->LoadField(access), input,
548 graph()->start(), graph()->start());
549 node->set_op(simplified()->NumberEqual());
550 node->ReplaceInput(0, length);
551 node->ReplaceInput(1, jsgraph()->ZeroConstant());
552 node->TrimInputCount(2);
553 NodeProperties::ReplaceWithValue(node, node, length);
554 return Changed(node);
560 Reduction JSTypedLowering::ReduceJSToBoolean(Node* node) {
561 Node* const input = node->InputAt(0);
562 Type* const input_type = NodeProperties::GetBounds(input).upper;
563 if (input_type->Is(Type::Boolean())) {
564 // JSToBoolean(x:boolean) => x
565 return Replace(input);
566 } else if (input_type->Is(Type::OrderedNumber())) {
567 // JSToBoolean(x:ordered-number) => BooleanNot(NumberEqual(x,#0))
568 node->set_op(simplified()->BooleanNot());
569 node->ReplaceInput(0, graph()->NewNode(simplified()->NumberEqual(), input,
570 jsgraph()->ZeroConstant()));
571 node->TrimInputCount(1);
572 return Changed(node);
573 } else if (input_type->Is(Type::String())) {
574 // JSToBoolean(x:string) => NumberLessThan(#0,x.length)
575 FieldAccess const access = AccessBuilder::ForStringLength();
576 // It is safe for the load to be effect-free (i.e. not linked into effect
577 // chain) because we assume String::length to be immutable.
578 Node* length = graph()->NewNode(simplified()->LoadField(access), input,
579 graph()->start(), graph()->start());
580 node->set_op(simplified()->NumberLessThan());
581 node->ReplaceInput(0, jsgraph()->ZeroConstant());
582 node->ReplaceInput(1, length);
583 node->TrimInputCount(2);
584 return Changed(node);
590 Reduction JSTypedLowering::ReduceJSToNumberInput(Node* input) {
591 if (input->opcode() == IrOpcode::kJSToNumber) {
592 // Recursively try to reduce the input first.
593 Reduction result = ReduceJSToNumber(input);
594 if (result.Changed()) return result;
595 return Changed(input); // JSToNumber(JSToNumber(x)) => JSToNumber(x)
597 // Check if we have a cached conversion.
598 Node* conversion = FindConversion<IrOpcode::kJSToNumber>(input);
599 if (conversion) return Replace(conversion);
600 Type* input_type = NodeProperties::GetBounds(input).upper;
601 if (input_type->Is(Type::Number())) {
602 // JSToNumber(x:number) => x
603 return Changed(input);
605 if (input_type->Is(Type::Undefined())) {
606 // JSToNumber(undefined) => #NaN
607 return Replace(jsgraph()->NaNConstant());
609 if (input_type->Is(Type::Null())) {
610 // JSToNumber(null) => #0
611 return Replace(jsgraph()->ZeroConstant());
613 if (input_type->Is(Type::Boolean())) {
614 // JSToNumber(x:boolean) => BooleanToNumber(x)
615 return Replace(graph()->NewNode(simplified()->BooleanToNumber(), input));
617 // TODO(turbofan): js-typed-lowering of ToNumber(x:string)
622 Reduction JSTypedLowering::ReduceJSToNumber(Node* node) {
623 // Try to reduce the input first.
624 Node* const input = node->InputAt(0);
625 Reduction reduction = ReduceJSToNumberInput(input);
626 if (reduction.Changed()) {
627 NodeProperties::ReplaceWithValue(node, reduction.replacement());
630 Type* const input_type = NodeProperties::GetBounds(input).upper;
631 if (input_type->Is(Type::PlainPrimitive())) {
632 if (input->opcode() == IrOpcode::kPhi) {
633 // JSToNumber(phi(x1,...,xn,control):plain-primitive,context)
634 // => phi(JSToNumber(x1,no-context),
636 // JSToNumber(xn,no-context),control)
637 int const input_count = input->InputCount() - 1;
638 Node* const control = input->InputAt(input_count);
639 DCHECK_LE(0, input_count);
640 DCHECK(NodeProperties::IsControl(control));
641 DCHECK(NodeProperties::GetBounds(node).upper->Is(Type::Number()));
642 DCHECK(!NodeProperties::GetBounds(input).upper->Is(Type::Number()));
643 RelaxEffectsAndControls(node);
644 node->set_op(common()->Phi(kMachAnyTagged, input_count));
645 for (int i = 0; i < input_count; ++i) {
646 // We must be very careful not to introduce cycles when pushing
647 // operations into phis. It is safe for {value}, since it appears
648 // as input to the phi that we are replacing, but it's not safe
649 // to simply reuse the context of the {node}. However, ToNumber()
650 // does not require a context anyways, so it's safe to discard it
651 // here and pass the dummy context.
652 Node* const value = ConvertPrimitiveToNumber(input->InputAt(i));
653 if (i < node->InputCount()) {
654 node->ReplaceInput(i, value);
656 node->AppendInput(graph()->zone(), value);
659 if (input_count < node->InputCount()) {
660 node->ReplaceInput(input_count, control);
662 node->AppendInput(graph()->zone(), control);
664 node->TrimInputCount(input_count + 1);
665 return Changed(node);
667 if (input->opcode() == IrOpcode::kSelect) {
668 // JSToNumber(select(c,x1,x2):plain-primitive,context)
669 // => select(c,JSToNumber(x1,no-context),JSToNumber(x2,no-context))
670 int const input_count = input->InputCount();
671 BranchHint const input_hint = SelectParametersOf(input->op()).hint();
672 DCHECK_EQ(3, input_count);
673 DCHECK(NodeProperties::GetBounds(node).upper->Is(Type::Number()));
674 DCHECK(!NodeProperties::GetBounds(input).upper->Is(Type::Number()));
675 RelaxEffectsAndControls(node);
676 node->set_op(common()->Select(kMachAnyTagged, input_hint));
677 node->ReplaceInput(0, input->InputAt(0));
678 for (int i = 1; i < input_count; ++i) {
679 // We must be very careful not to introduce cycles when pushing
680 // operations into selects. It is safe for {value}, since it appears
681 // as input to the select that we are replacing, but it's not safe
682 // to simply reuse the context of the {node}. However, ToNumber()
683 // does not require a context anyways, so it's safe to discard it
684 // here and pass the dummy context.
685 Node* const value = ConvertPrimitiveToNumber(input->InputAt(i));
686 node->ReplaceInput(i, value);
688 node->TrimInputCount(input_count);
689 return Changed(node);
691 // Remember this conversion.
692 InsertConversion(node);
693 if (NodeProperties::GetContextInput(node) !=
694 jsgraph()->NoContextConstant() ||
695 NodeProperties::GetEffectInput(node) != graph()->start() ||
696 NodeProperties::GetControlInput(node) != graph()->start()) {
697 // JSToNumber(x:plain-primitive,context,effect,control)
698 // => JSToNumber(x,no-context,start,start)
699 RelaxEffectsAndControls(node);
700 NodeProperties::ReplaceContextInput(node, jsgraph()->NoContextConstant());
701 NodeProperties::ReplaceControlInput(node, graph()->start());
702 NodeProperties::ReplaceEffectInput(node, graph()->start());
703 if (FLAG_turbo_deoptimization) {
704 DCHECK_EQ(1, OperatorProperties::GetFrameStateInputCount(node->op()));
705 NodeProperties::ReplaceFrameStateInput(node, 0,
706 jsgraph()->EmptyFrameState());
708 return Changed(node);
715 Reduction JSTypedLowering::ReduceJSToStringInput(Node* input) {
716 if (input->opcode() == IrOpcode::kJSToString) {
717 // Recursively try to reduce the input first.
718 Reduction result = ReduceJSToString(input);
719 if (result.Changed()) return result;
720 return Changed(input); // JSToString(JSToString(x)) => JSToString(x)
722 Type* input_type = NodeProperties::GetBounds(input).upper;
723 if (input_type->Is(Type::String())) {
724 return Changed(input); // JSToString(x:string) => x
726 if (input_type->Is(Type::Undefined())) {
727 return Replace(jsgraph()->HeapConstant(factory()->undefined_string()));
729 if (input_type->Is(Type::Null())) {
730 return Replace(jsgraph()->HeapConstant(factory()->null_string()));
732 // TODO(turbofan): js-typed-lowering of ToString(x:boolean)
733 // TODO(turbofan): js-typed-lowering of ToString(x:number)
738 Reduction JSTypedLowering::ReduceJSToString(Node* node) {
739 // Try to reduce the input first.
740 Node* const input = node->InputAt(0);
741 Reduction reduction = ReduceJSToStringInput(input);
742 if (reduction.Changed()) {
743 NodeProperties::ReplaceWithValue(node, reduction.replacement());
750 Reduction JSTypedLowering::ReduceJSLoadProperty(Node* node) {
751 Node* key = NodeProperties::GetValueInput(node, 1);
752 Node* base = NodeProperties::GetValueInput(node, 0);
753 Type* key_type = NodeProperties::GetBounds(key).upper;
754 // TODO(mstarzinger): This lowering is not correct if:
755 // a) The typed array or it's buffer is neutered.
756 HeapObjectMatcher<Object> mbase(base);
757 if (mbase.HasValue() && mbase.Value().handle()->IsJSTypedArray()) {
758 Handle<JSTypedArray> const array =
759 Handle<JSTypedArray>::cast(mbase.Value().handle());
760 array->GetBuffer()->set_is_neuterable(false);
761 BufferAccess const access(array->type());
762 size_t const k = ElementSizeLog2Of(access.machine_type());
763 double const byte_length = array->byte_length()->Number();
764 CHECK_LT(k, arraysize(shifted_int32_ranges_));
765 if (IsExternalArrayElementsKind(array->map()->elements_kind()) &&
766 key_type->Is(shifted_int32_ranges_[k]) && byte_length <= kMaxInt) {
767 // JSLoadProperty(typed-array, int32)
768 Handle<ExternalArray> elements =
769 Handle<ExternalArray>::cast(handle(array->elements()));
770 Node* buffer = jsgraph()->PointerConstant(elements->external_pointer());
771 Node* length = jsgraph()->Constant(byte_length);
772 Node* effect = NodeProperties::GetEffectInput(node);
773 Node* control = NodeProperties::GetControlInput(node);
774 // Check if we can avoid the bounds check.
775 if (key_type->Min() >= 0 && key_type->Max() < array->length()->Number()) {
776 Node* load = graph()->NewNode(
777 simplified()->LoadElement(
778 AccessBuilder::ForTypedArrayElement(array->type(), true)),
779 buffer, key, effect, control);
780 return ReplaceEagerly(node, load);
782 // Compute byte offset.
783 Node* offset = Word32Shl(key, static_cast<int>(k));
784 Node* load = graph()->NewNode(simplified()->LoadBuffer(access), buffer,
785 offset, length, effect, control);
786 return ReplaceEagerly(node, load);
793 Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) {
794 Node* key = NodeProperties::GetValueInput(node, 1);
795 Node* base = NodeProperties::GetValueInput(node, 0);
796 Node* value = NodeProperties::GetValueInput(node, 2);
797 Type* key_type = NodeProperties::GetBounds(key).upper;
798 Type* value_type = NodeProperties::GetBounds(value).upper;
799 // TODO(mstarzinger): This lowering is not correct if:
800 // a) The typed array or its buffer is neutered.
801 HeapObjectMatcher<Object> mbase(base);
802 if (mbase.HasValue() && mbase.Value().handle()->IsJSTypedArray()) {
803 Handle<JSTypedArray> const array =
804 Handle<JSTypedArray>::cast(mbase.Value().handle());
805 array->GetBuffer()->set_is_neuterable(false);
806 BufferAccess const access(array->type());
807 size_t const k = ElementSizeLog2Of(access.machine_type());
808 double const byte_length = array->byte_length()->Number();
809 CHECK_LT(k, arraysize(shifted_int32_ranges_));
810 if (IsExternalArrayElementsKind(array->map()->elements_kind()) &&
811 access.external_array_type() != kExternalUint8ClampedArray &&
812 key_type->Is(shifted_int32_ranges_[k]) && byte_length <= kMaxInt) {
813 // JSLoadProperty(typed-array, int32)
814 Handle<ExternalArray> elements =
815 Handle<ExternalArray>::cast(handle(array->elements()));
816 Node* buffer = jsgraph()->PointerConstant(elements->external_pointer());
817 Node* length = jsgraph()->Constant(byte_length);
818 Node* context = NodeProperties::GetContextInput(node);
819 Node* effect = NodeProperties::GetEffectInput(node);
820 Node* control = NodeProperties::GetControlInput(node);
821 // Convert to a number first.
822 if (!value_type->Is(Type::Number())) {
823 Reduction number_reduction = ReduceJSToNumberInput(value);
824 if (number_reduction.Changed()) {
825 value = number_reduction.replacement();
827 DCHECK(FLAG_turbo_deoptimization ==
828 (OperatorProperties::GetFrameStateInputCount(
829 javascript()->ToNumber()) == 1));
830 if (FLAG_turbo_deoptimization) {
831 Node* frame_state_for_to_number =
832 NodeProperties::GetFrameStateInput(node, 1);
834 graph()->NewNode(javascript()->ToNumber(), value, context,
835 frame_state_for_to_number, effect, control);
837 value = effect = graph()->NewNode(javascript()->ToNumber(), value,
838 context, effect, control);
842 // For integer-typed arrays, convert to the integer type.
843 if (TypeOf(access.machine_type()) == kTypeInt32 &&
844 !value_type->Is(Type::Signed32())) {
845 value = graph()->NewNode(simplified()->NumberToInt32(), value);
846 } else if (TypeOf(access.machine_type()) == kTypeUint32 &&
847 !value_type->Is(Type::Unsigned32())) {
848 value = graph()->NewNode(simplified()->NumberToUint32(), value);
850 // Check if we can avoid the bounds check.
851 if (key_type->Min() >= 0 && key_type->Max() < array->length()->Number()) {
852 node->set_op(simplified()->StoreElement(
853 AccessBuilder::ForTypedArrayElement(array->type(), true)));
854 node->ReplaceInput(0, buffer);
855 DCHECK_EQ(key, node->InputAt(1));
856 node->ReplaceInput(2, value);
857 node->ReplaceInput(3, effect);
858 node->ReplaceInput(4, control);
859 node->TrimInputCount(5);
861 return Changed(node);
863 // Compute byte offset.
864 Node* offset = Word32Shl(key, static_cast<int>(k));
865 // Turn into a StoreBuffer operation.
866 node->set_op(simplified()->StoreBuffer(access));
867 node->ReplaceInput(0, buffer);
868 node->ReplaceInput(1, offset);
869 node->ReplaceInput(2, length);
870 node->ReplaceInput(3, value);
871 node->ReplaceInput(4, effect);
872 node->ReplaceInput(5, control);
873 node->TrimInputCount(6);
875 return Changed(node);
882 Reduction JSTypedLowering::ReduceJSLoadContext(Node* node) {
883 DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
884 ContextAccess const& access = ContextAccessOf(node->op());
885 Node* const effect = NodeProperties::GetEffectInput(node);
886 Node* const control = graph()->start();
887 for (size_t i = 0; i < access.depth(); ++i) {
890 simplified()->LoadField(
891 AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX)),
892 NodeProperties::GetValueInput(node, 0), effect, control));
895 simplified()->LoadField(AccessBuilder::ForContextSlot(access.index())));
896 node->ReplaceInput(1, effect);
897 node->ReplaceInput(2, control);
898 DCHECK_EQ(3, node->InputCount());
899 return Changed(node);
903 Reduction JSTypedLowering::ReduceJSStoreContext(Node* node) {
904 DCHECK_EQ(IrOpcode::kJSStoreContext, node->opcode());
905 ContextAccess const& access = ContextAccessOf(node->op());
906 Node* const effect = NodeProperties::GetEffectInput(node);
907 Node* const control = graph()->start();
908 for (size_t i = 0; i < access.depth(); ++i) {
911 simplified()->LoadField(
912 AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX)),
913 NodeProperties::GetValueInput(node, 0), effect, control));
916 simplified()->StoreField(AccessBuilder::ForContextSlot(access.index())));
917 node->RemoveInput(2);
918 DCHECK_EQ(4, node->InputCount());
919 return Changed(node);
923 Reduction JSTypedLowering::Reduce(Node* node) {
924 // Check if the output type is a singleton. In that case we already know the
925 // result value and can simply replace the node if it's eliminable.
926 if (!NodeProperties::IsConstant(node) && NodeProperties::IsTyped(node) &&
927 node->op()->HasProperty(Operator::kEliminatable)) {
928 Type* upper = NodeProperties::GetBounds(node).upper;
929 if (upper->IsConstant()) {
930 Node* replacement = jsgraph()->Constant(upper->AsConstant()->Value());
931 NodeProperties::ReplaceWithValue(node, replacement);
932 return Changed(replacement);
933 } else if (upper->Is(Type::MinusZero())) {
934 Node* replacement = jsgraph()->Constant(factory()->minus_zero_value());
935 NodeProperties::ReplaceWithValue(node, replacement);
936 return Changed(replacement);
937 } else if (upper->Is(Type::NaN())) {
938 Node* replacement = jsgraph()->NaNConstant();
939 NodeProperties::ReplaceWithValue(node, replacement);
940 return Changed(replacement);
941 } else if (upper->Is(Type::Null())) {
942 Node* replacement = jsgraph()->NullConstant();
943 NodeProperties::ReplaceWithValue(node, replacement);
944 return Changed(replacement);
945 } else if (upper->Is(Type::PlainNumber()) && upper->Min() == upper->Max()) {
946 Node* replacement = jsgraph()->Constant(upper->Min());
947 NodeProperties::ReplaceWithValue(node, replacement);
948 return Changed(replacement);
949 } else if (upper->Is(Type::Undefined())) {
950 Node* replacement = jsgraph()->UndefinedConstant();
951 NodeProperties::ReplaceWithValue(node, replacement);
952 return Changed(replacement);
955 switch (node->opcode()) {
956 case IrOpcode::kJSEqual:
957 return ReduceJSEqual(node, false);
958 case IrOpcode::kJSNotEqual:
959 return ReduceJSEqual(node, true);
960 case IrOpcode::kJSStrictEqual:
961 return ReduceJSStrictEqual(node, false);
962 case IrOpcode::kJSStrictNotEqual:
963 return ReduceJSStrictEqual(node, true);
964 case IrOpcode::kJSLessThan: // fall through
965 case IrOpcode::kJSGreaterThan: // fall through
966 case IrOpcode::kJSLessThanOrEqual: // fall through
967 case IrOpcode::kJSGreaterThanOrEqual:
968 return ReduceJSComparison(node);
969 case IrOpcode::kJSBitwiseOr:
970 return ReduceInt32Binop(node, machine()->Word32Or());
971 case IrOpcode::kJSBitwiseXor:
972 return ReduceInt32Binop(node, machine()->Word32Xor());
973 case IrOpcode::kJSBitwiseAnd:
974 return ReduceInt32Binop(node, machine()->Word32And());
975 case IrOpcode::kJSShiftLeft:
976 return ReduceUI32Shift(node, kSigned, machine()->Word32Shl());
977 case IrOpcode::kJSShiftRight:
978 return ReduceUI32Shift(node, kSigned, machine()->Word32Sar());
979 case IrOpcode::kJSShiftRightLogical:
980 return ReduceUI32Shift(node, kUnsigned, machine()->Word32Shr());
981 case IrOpcode::kJSAdd:
982 return ReduceJSAdd(node);
983 case IrOpcode::kJSSubtract:
984 return ReduceNumberBinop(node, simplified()->NumberSubtract());
985 case IrOpcode::kJSMultiply:
986 return ReduceNumberBinop(node, simplified()->NumberMultiply());
987 case IrOpcode::kJSDivide:
988 return ReduceNumberBinop(node, simplified()->NumberDivide());
989 case IrOpcode::kJSModulus:
990 return ReduceNumberBinop(node, simplified()->NumberModulus());
991 case IrOpcode::kJSUnaryNot:
992 return ReduceJSUnaryNot(node);
993 case IrOpcode::kJSToBoolean:
994 return ReduceJSToBoolean(node);
995 case IrOpcode::kJSToNumber:
996 return ReduceJSToNumber(node);
997 case IrOpcode::kJSToString:
998 return ReduceJSToString(node);
999 case IrOpcode::kJSLoadProperty:
1000 return ReduceJSLoadProperty(node);
1001 case IrOpcode::kJSStoreProperty:
1002 return ReduceJSStoreProperty(node);
1003 case IrOpcode::kJSLoadContext:
1004 return ReduceJSLoadContext(node);
1005 case IrOpcode::kJSStoreContext:
1006 return ReduceJSStoreContext(node);
1014 Node* JSTypedLowering::ConvertPrimitiveToNumber(Node* input) {
1015 DCHECK(NodeProperties::GetBounds(input).upper->Is(Type::PlainPrimitive()));
1016 // Avoid inserting too many eager ToNumber() operations.
1017 Reduction const reduction = ReduceJSToNumberInput(input);
1018 if (reduction.Changed()) return reduction.replacement();
1019 // TODO(jarin) Use PlainPrimitiveToNumber once we have it.
1020 Node* const conversion =
1021 FLAG_turbo_deoptimization
1022 ? graph()->NewNode(javascript()->ToNumber(), input,
1023 jsgraph()->NoContextConstant(),
1024 jsgraph()->EmptyFrameState(), graph()->start(),
1026 : graph()->NewNode(javascript()->ToNumber(), input,
1027 jsgraph()->NoContextConstant(), graph()->start(),
1029 InsertConversion(conversion);
1034 template <IrOpcode::Value kOpcode>
1035 Node* JSTypedLowering::FindConversion(Node* input) {
1036 size_t const input_id = input->id();
1037 if (input_id < conversions_.size()) {
1038 Node* const conversion = conversions_[input_id];
1039 if (conversion && conversion->opcode() == kOpcode) {
1047 void JSTypedLowering::InsertConversion(Node* conversion) {
1048 DCHECK(conversion->opcode() == IrOpcode::kJSToNumber);
1049 size_t const input_id = conversion->InputAt(0)->id();
1050 if (input_id >= conversions_.size()) {
1051 conversions_.resize(2 * input_id + 1);
1053 conversions_[input_id] = conversion;
1057 Node* JSTypedLowering::Word32Shl(Node* const lhs, int32_t const rhs) {
1058 if (rhs == 0) return lhs;
1059 return graph()->NewNode(machine()->Word32Shl(), lhs,
1060 jsgraph()->Int32Constant(rhs));
1064 Factory* JSTypedLowering::factory() const { return jsgraph()->factory(); }
1067 Graph* JSTypedLowering::graph() const { return jsgraph()->graph(); }
1070 JSOperatorBuilder* JSTypedLowering::javascript() const {
1071 return jsgraph()->javascript();
1075 CommonOperatorBuilder* JSTypedLowering::common() const {
1076 return jsgraph()->common();
1080 MachineOperatorBuilder* JSTypedLowering::machine() const {
1081 return jsgraph()->machine();
1084 } // namespace compiler
1085 } // namespace internal