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/access-builder.h"
6#include "src/compiler/graph-inl.h"
7#include "src/compiler/js-builtin-reducer.h"
8#include "src/compiler/js-typed-lowering.h"
9#include "src/compiler/node-aux-data-inl.h"
10#include "src/compiler/node-properties-inl.h"
11#include "src/types.h"
12
13namespace v8 {
14namespace internal {
15namespace compiler {
16
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
20// - relax effects for ToNumber(mixed)
21
22
23// Relax the effects of {node} by immediately replacing effect uses of {node}
24// with the effect input to {node}.
25// TODO(turbofan): replace the effect input to {node} with {graph->start()}.
26// TODO(titzer): move into a GraphEditor?
27static void RelaxEffects(Node* node) {
28  NodeProperties::ReplaceWithValue(node, node, NULL);
29}
30
31
32JSTypedLowering::~JSTypedLowering() {}
33
34
35Reduction JSTypedLowering::ReplaceEagerly(Node* old, Node* node) {
36  NodeProperties::ReplaceWithValue(old, node, node);
37  return Changed(node);
38}
39
40
41// A helper class to simplify the process of reducing a single binop node with a
42// JSOperator. This class manages the rewriting of context, control, and effect
43// dependencies during lowering of a binop and contains numerous helper
44// functions for matching the types of inputs to an operation.
45class JSBinopReduction {
46 public:
47  JSBinopReduction(JSTypedLowering* lowering, Node* node)
48      : lowering_(lowering),
49        node_(node),
50        left_type_(NodeProperties::GetBounds(node->InputAt(0)).upper),
51        right_type_(NodeProperties::GetBounds(node->InputAt(1)).upper) {}
52
53  void ConvertInputsToNumber() {
54    node_->ReplaceInput(0, ConvertToNumber(left()));
55    node_->ReplaceInput(1, ConvertToNumber(right()));
56  }
57
58  void ConvertInputsToInt32(bool left_signed, bool right_signed) {
59    node_->ReplaceInput(0, ConvertToI32(left_signed, left()));
60    node_->ReplaceInput(1, ConvertToI32(right_signed, right()));
61  }
62
63  void ConvertInputsToString() {
64    node_->ReplaceInput(0, ConvertToString(left()));
65    node_->ReplaceInput(1, ConvertToString(right()));
66  }
67
68  // Convert inputs for bitwise shift operation (ES5 spec 11.7).
69  void ConvertInputsForShift(bool left_signed) {
70    node_->ReplaceInput(0, ConvertToI32(left_signed, left()));
71    Node* rnum = ConvertToI32(false, right());
72    node_->ReplaceInput(1, graph()->NewNode(machine()->Word32And(), rnum,
73                                            jsgraph()->Int32Constant(0x1F)));
74  }
75
76  void SwapInputs() {
77    Node* l = left();
78    Node* r = right();
79    node_->ReplaceInput(0, r);
80    node_->ReplaceInput(1, l);
81    std::swap(left_type_, right_type_);
82  }
83
84  // Remove all effect and control inputs and outputs to this node and change
85  // to the pure operator {op}, possibly inserting a boolean inversion.
86  Reduction ChangeToPureOperator(const Operator* op, bool invert = false) {
87    DCHECK_EQ(0, OperatorProperties::GetEffectInputCount(op));
88    DCHECK_EQ(false, OperatorProperties::HasContextInput(op));
89    DCHECK_EQ(0, OperatorProperties::GetControlInputCount(op));
90    DCHECK_EQ(2, OperatorProperties::GetValueInputCount(op));
91
92    // Remove the effects from the node, if any, and update its effect usages.
93    if (OperatorProperties::GetEffectInputCount(node_->op()) > 0) {
94      RelaxEffects(node_);
95    }
96    // Remove the inputs corresponding to context, effect, and control.
97    NodeProperties::RemoveNonValueInputs(node_);
98    // Finally, update the operator to the new one.
99    node_->set_op(op);
100
101    if (invert) {
102      // Insert an boolean not to invert the value.
103      Node* value = graph()->NewNode(simplified()->BooleanNot(), node_);
104      node_->ReplaceUses(value);
105      // Note: ReplaceUses() smashes all uses, so smash it back here.
106      value->ReplaceInput(0, node_);
107      return lowering_->ReplaceWith(value);
108    }
109    return lowering_->Changed(node_);
110  }
111
112  bool OneInputIs(Type* t) { return left_type_->Is(t) || right_type_->Is(t); }
113
114  bool BothInputsAre(Type* t) {
115    return left_type_->Is(t) && right_type_->Is(t);
116  }
117
118  bool OneInputCannotBe(Type* t) {
119    return !left_type_->Maybe(t) || !right_type_->Maybe(t);
120  }
121
122  bool NeitherInputCanBe(Type* t) {
123    return !left_type_->Maybe(t) && !right_type_->Maybe(t);
124  }
125
126  Node* effect() { return NodeProperties::GetEffectInput(node_); }
127  Node* control() { return NodeProperties::GetControlInput(node_); }
128  Node* context() { return NodeProperties::GetContextInput(node_); }
129  Node* left() { return NodeProperties::GetValueInput(node_, 0); }
130  Node* right() { return NodeProperties::GetValueInput(node_, 1); }
131  Type* left_type() { return left_type_; }
132  Type* right_type() { return right_type_; }
133
134  SimplifiedOperatorBuilder* simplified() { return lowering_->simplified(); }
135  Graph* graph() { return lowering_->graph(); }
136  JSGraph* jsgraph() { return lowering_->jsgraph(); }
137  JSOperatorBuilder* javascript() { return lowering_->javascript(); }
138  MachineOperatorBuilder* machine() { return lowering_->machine(); }
139
140 private:
141  JSTypedLowering* lowering_;  // The containing lowering instance.
142  Node* node_;                 // The original node.
143  Type* left_type_;            // Cache of the left input's type.
144  Type* right_type_;           // Cache of the right input's type.
145
146  Node* ConvertToString(Node* node) {
147    // Avoid introducing too many eager ToString() operations.
148    Reduction reduced = lowering_->ReduceJSToStringInput(node);
149    if (reduced.Changed()) return reduced.replacement();
150    Node* n = graph()->NewNode(javascript()->ToString(), node, context(),
151                               effect(), control());
152    update_effect(n);
153    return n;
154  }
155
156  Node* ConvertToNumber(Node* node) {
157    // Avoid introducing too many eager ToNumber() operations.
158    Reduction reduced = lowering_->ReduceJSToNumberInput(node);
159    if (reduced.Changed()) return reduced.replacement();
160    Node* n = graph()->NewNode(javascript()->ToNumber(), node, context(),
161                               effect(), control());
162    update_effect(n);
163    return n;
164  }
165
166  // Try to narrowing a double or number operation to an Int32 operation.
167  bool TryNarrowingToI32(Type* type, Node* node) {
168    switch (node->opcode()) {
169      case IrOpcode::kFloat64Add:
170      case IrOpcode::kNumberAdd: {
171        JSBinopReduction r(lowering_, node);
172        if (r.BothInputsAre(Type::Integral32())) {
173          node->set_op(lowering_->machine()->Int32Add());
174          // TODO(titzer): narrow bounds instead of overwriting.
175          NodeProperties::SetBounds(node, Bounds(type));
176          return true;
177        }
178      }
179      case IrOpcode::kFloat64Sub:
180      case IrOpcode::kNumberSubtract: {
181        JSBinopReduction r(lowering_, node);
182        if (r.BothInputsAre(Type::Integral32())) {
183          node->set_op(lowering_->machine()->Int32Sub());
184          // TODO(titzer): narrow bounds instead of overwriting.
185          NodeProperties::SetBounds(node, Bounds(type));
186          return true;
187        }
188      }
189      default:
190        return false;
191    }
192  }
193
194  Node* ConvertToI32(bool is_signed, Node* node) {
195    Type* type = is_signed ? Type::Signed32() : Type::Unsigned32();
196    if (node->OwnedBy(node_)) {
197      // If this node {node_} has the only edge to {node}, then try narrowing
198      // its operation to an Int32 add or subtract.
199      if (TryNarrowingToI32(type, node)) return node;
200    } else {
201      // Otherwise, {node} has multiple uses. Leave it as is and let the
202      // further lowering passes deal with it, which use a full backwards
203      // fixpoint.
204    }
205
206    // Avoid introducing too many eager NumberToXXnt32() operations.
207    node = ConvertToNumber(node);
208    Type* input_type = NodeProperties::GetBounds(node).upper;
209
210    if (input_type->Is(type)) return node;  // already in the value range.
211
212    const Operator* op = is_signed ? simplified()->NumberToInt32()
213                                   : simplified()->NumberToUint32();
214    Node* n = graph()->NewNode(op, node);
215    return n;
216  }
217
218  void update_effect(Node* effect) {
219    NodeProperties::ReplaceEffectInput(node_, effect);
220  }
221};
222
223
224Reduction JSTypedLowering::ReduceJSAdd(Node* node) {
225  JSBinopReduction r(this, node);
226  if (r.BothInputsAre(Type::Number())) {
227    // JSAdd(x:number, y:number) => NumberAdd(x, y)
228    return r.ChangeToPureOperator(simplified()->NumberAdd());
229  }
230  Type* maybe_string = Type::Union(Type::String(), Type::Receiver(), zone());
231  if (r.NeitherInputCanBe(maybe_string)) {
232    // JSAdd(x:-string, y:-string) => NumberAdd(ToNumber(x), ToNumber(y))
233    r.ConvertInputsToNumber();
234    return r.ChangeToPureOperator(simplified()->NumberAdd());
235  }
236#if 0
237  // TODO(turbofan): Lowering of StringAdd is disabled for now because:
238  //   a) The inserted ToString operation screws up valueOf vs. toString order.
239  //   b) Deoptimization at ToString doesn't have corresponding bailout id.
240  //   c) Our current StringAddStub is actually non-pure and requires context.
241  if (r.OneInputIs(Type::String())) {
242    // JSAdd(x:string, y:string) => StringAdd(x, y)
243    // JSAdd(x:string, y) => StringAdd(x, ToString(y))
244    // JSAdd(x, y:string) => StringAdd(ToString(x), y)
245    r.ConvertInputsToString();
246    return r.ChangeToPureOperator(simplified()->StringAdd());
247  }
248#endif
249  return NoChange();
250}
251
252
253Reduction JSTypedLowering::ReduceNumberBinop(Node* node,
254                                             const Operator* numberOp) {
255  JSBinopReduction r(this, node);
256  if (r.OneInputIs(Type::Primitive())) {
257    // If at least one input is a primitive, then insert appropriate conversions
258    // to number and reduce this operator to the given numeric one.
259    // TODO(turbofan): make this heuristic configurable for code size.
260    r.ConvertInputsToNumber();
261    return r.ChangeToPureOperator(numberOp);
262  }
263  // TODO(turbofan): relax/remove the effects of this operator in other cases.
264  return NoChange();
265}
266
267
268Reduction JSTypedLowering::ReduceI32Binop(Node* node, bool left_signed,
269                                          bool right_signed,
270                                          const Operator* intOp) {
271  JSBinopReduction r(this, node);
272  // TODO(titzer): some Smi bitwise operations don't really require going
273  // all the way to int32, which can save tagging/untagging for some operations
274  // on some platforms.
275  // TODO(turbofan): make this heuristic configurable for code size.
276  r.ConvertInputsToInt32(left_signed, right_signed);
277  return r.ChangeToPureOperator(intOp);
278}
279
280
281Reduction JSTypedLowering::ReduceI32Shift(Node* node, bool left_signed,
282                                          const Operator* shift_op) {
283  JSBinopReduction r(this, node);
284  r.ConvertInputsForShift(left_signed);
285  return r.ChangeToPureOperator(shift_op);
286}
287
288
289Reduction JSTypedLowering::ReduceJSComparison(Node* node) {
290  JSBinopReduction r(this, node);
291  if (r.BothInputsAre(Type::String())) {
292    // If both inputs are definitely strings, perform a string comparison.
293    const Operator* stringOp;
294    switch (node->opcode()) {
295      case IrOpcode::kJSLessThan:
296        stringOp = simplified()->StringLessThan();
297        break;
298      case IrOpcode::kJSGreaterThan:
299        stringOp = simplified()->StringLessThan();
300        r.SwapInputs();  // a > b => b < a
301        break;
302      case IrOpcode::kJSLessThanOrEqual:
303        stringOp = simplified()->StringLessThanOrEqual();
304        break;
305      case IrOpcode::kJSGreaterThanOrEqual:
306        stringOp = simplified()->StringLessThanOrEqual();
307        r.SwapInputs();  // a >= b => b <= a
308        break;
309      default:
310        return NoChange();
311    }
312    return r.ChangeToPureOperator(stringOp);
313  }
314  Type* maybe_string = Type::Union(Type::String(), Type::Receiver(), zone());
315  if (r.OneInputCannotBe(maybe_string)) {
316    // If one input cannot be a string, then emit a number comparison.
317    const Operator* less_than;
318    const Operator* less_than_or_equal;
319    if (r.BothInputsAre(Type::Unsigned32())) {
320      less_than = machine()->Uint32LessThan();
321      less_than_or_equal = machine()->Uint32LessThanOrEqual();
322    } else if (r.BothInputsAre(Type::Signed32())) {
323      less_than = machine()->Int32LessThan();
324      less_than_or_equal = machine()->Int32LessThanOrEqual();
325    } else {
326      // TODO(turbofan): mixed signed/unsigned int32 comparisons.
327      r.ConvertInputsToNumber();
328      less_than = simplified()->NumberLessThan();
329      less_than_or_equal = simplified()->NumberLessThanOrEqual();
330    }
331    const Operator* comparison;
332    switch (node->opcode()) {
333      case IrOpcode::kJSLessThan:
334        comparison = less_than;
335        break;
336      case IrOpcode::kJSGreaterThan:
337        comparison = less_than;
338        r.SwapInputs();  // a > b => b < a
339        break;
340      case IrOpcode::kJSLessThanOrEqual:
341        comparison = less_than_or_equal;
342        break;
343      case IrOpcode::kJSGreaterThanOrEqual:
344        comparison = less_than_or_equal;
345        r.SwapInputs();  // a >= b => b <= a
346        break;
347      default:
348        return NoChange();
349    }
350    return r.ChangeToPureOperator(comparison);
351  }
352  // TODO(turbofan): relax/remove effects of this operator in other cases.
353  return NoChange();  // Keep a generic comparison.
354}
355
356
357Reduction JSTypedLowering::ReduceJSEqual(Node* node, bool invert) {
358  JSBinopReduction r(this, node);
359
360  if (r.BothInputsAre(Type::Number())) {
361    return r.ChangeToPureOperator(simplified()->NumberEqual(), invert);
362  }
363  if (r.BothInputsAre(Type::String())) {
364    return r.ChangeToPureOperator(simplified()->StringEqual(), invert);
365  }
366  if (r.BothInputsAre(Type::Receiver())) {
367    return r.ChangeToPureOperator(
368        simplified()->ReferenceEqual(Type::Receiver()), invert);
369  }
370  // TODO(turbofan): js-typed-lowering of Equal(undefined)
371  // TODO(turbofan): js-typed-lowering of Equal(null)
372  // TODO(turbofan): js-typed-lowering of Equal(boolean)
373  return NoChange();
374}
375
376
377Reduction JSTypedLowering::ReduceJSStrictEqual(Node* node, bool invert) {
378  JSBinopReduction r(this, node);
379  if (r.left() == r.right()) {
380    // x === x is always true if x != NaN
381    if (!r.left_type()->Maybe(Type::NaN())) {
382      return ReplaceEagerly(node, invert ? jsgraph()->FalseConstant()
383                                         : jsgraph()->TrueConstant());
384    }
385  }
386  if (!r.left_type()->Maybe(r.right_type())) {
387    // Type intersection is empty; === is always false unless both
388    // inputs could be strings (one internalized and one not).
389    if (r.OneInputCannotBe(Type::String())) {
390      return ReplaceEagerly(node, invert ? jsgraph()->TrueConstant()
391                                         : jsgraph()->FalseConstant());
392    }
393  }
394  if (r.OneInputIs(Type::Undefined())) {
395    return r.ChangeToPureOperator(
396        simplified()->ReferenceEqual(Type::Undefined()), invert);
397  }
398  if (r.OneInputIs(Type::Null())) {
399    return r.ChangeToPureOperator(simplified()->ReferenceEqual(Type::Null()),
400                                  invert);
401  }
402  if (r.OneInputIs(Type::Boolean())) {
403    return r.ChangeToPureOperator(simplified()->ReferenceEqual(Type::Boolean()),
404                                  invert);
405  }
406  if (r.OneInputIs(Type::Object())) {
407    return r.ChangeToPureOperator(simplified()->ReferenceEqual(Type::Object()),
408                                  invert);
409  }
410  if (r.OneInputIs(Type::Receiver())) {
411    return r.ChangeToPureOperator(
412        simplified()->ReferenceEqual(Type::Receiver()), invert);
413  }
414  if (r.BothInputsAre(Type::String())) {
415    return r.ChangeToPureOperator(simplified()->StringEqual(), invert);
416  }
417  if (r.BothInputsAre(Type::Number())) {
418    return r.ChangeToPureOperator(simplified()->NumberEqual(), invert);
419  }
420  // TODO(turbofan): js-typed-lowering of StrictEqual(mixed types)
421  return NoChange();
422}
423
424
425Reduction JSTypedLowering::ReduceJSToNumberInput(Node* input) {
426  if (input->opcode() == IrOpcode::kJSToNumber) {
427    // Recursively try to reduce the input first.
428    Reduction result = ReduceJSToNumberInput(input->InputAt(0));
429    if (result.Changed()) {
430      RelaxEffects(input);
431      return result;
432    }
433    return Changed(input);  // JSToNumber(JSToNumber(x)) => JSToNumber(x)
434  }
435  Type* input_type = NodeProperties::GetBounds(input).upper;
436  if (input_type->Is(Type::Number())) {
437    // JSToNumber(x:number) => x
438    return Changed(input);
439  }
440  if (input_type->Is(Type::Undefined())) {
441    // JSToNumber(undefined) => #NaN
442    return ReplaceWith(jsgraph()->NaNConstant());
443  }
444  if (input_type->Is(Type::Null())) {
445    // JSToNumber(null) => #0
446    return ReplaceWith(jsgraph()->ZeroConstant());
447  }
448  if (input_type->Is(Type::Boolean())) {
449    // JSToNumber(x:boolean) => BooleanToNumber(x)
450    return ReplaceWith(
451        graph()->NewNode(simplified()->BooleanToNumber(), input));
452  }
453  // TODO(turbofan): js-typed-lowering of ToNumber(x:string)
454  return NoChange();
455}
456
457
458Reduction JSTypedLowering::ReduceJSToStringInput(Node* input) {
459  if (input->opcode() == IrOpcode::kJSToString) {
460    // Recursively try to reduce the input first.
461    Reduction result = ReduceJSToStringInput(input->InputAt(0));
462    if (result.Changed()) {
463      RelaxEffects(input);
464      return result;
465    }
466    return Changed(input);  // JSToString(JSToString(x)) => JSToString(x)
467  }
468  Type* input_type = NodeProperties::GetBounds(input).upper;
469  if (input_type->Is(Type::String())) {
470    return Changed(input);  // JSToString(x:string) => x
471  }
472  if (input_type->Is(Type::Undefined())) {
473    return ReplaceWith(jsgraph()->HeapConstant(
474        graph()->zone()->isolate()->factory()->undefined_string()));
475  }
476  if (input_type->Is(Type::Null())) {
477    return ReplaceWith(jsgraph()->HeapConstant(
478        graph()->zone()->isolate()->factory()->null_string()));
479  }
480  // TODO(turbofan): js-typed-lowering of ToString(x:boolean)
481  // TODO(turbofan): js-typed-lowering of ToString(x:number)
482  return NoChange();
483}
484
485
486Reduction JSTypedLowering::ReduceJSToBooleanInput(Node* input) {
487  if (input->opcode() == IrOpcode::kJSToBoolean) {
488    // Recursively try to reduce the input first.
489    Reduction result = ReduceJSToBooleanInput(input->InputAt(0));
490    if (result.Changed()) {
491      RelaxEffects(input);
492      return result;
493    }
494    return Changed(input);  // JSToBoolean(JSToBoolean(x)) => JSToBoolean(x)
495  }
496  Type* input_type = NodeProperties::GetBounds(input).upper;
497  if (input_type->Is(Type::Boolean())) {
498    return Changed(input);  // JSToBoolean(x:boolean) => x
499  }
500  if (input_type->Is(Type::Undefined())) {
501    // JSToBoolean(undefined) => #false
502    return ReplaceWith(jsgraph()->FalseConstant());
503  }
504  if (input_type->Is(Type::Null())) {
505    // JSToBoolean(null) => #false
506    return ReplaceWith(jsgraph()->FalseConstant());
507  }
508  if (input_type->Is(Type::DetectableReceiver())) {
509    // JSToBoolean(x:detectable) => #true
510    return ReplaceWith(jsgraph()->TrueConstant());
511  }
512  if (input_type->Is(Type::Undetectable())) {
513    // JSToBoolean(x:undetectable) => #false
514    return ReplaceWith(jsgraph()->FalseConstant());
515  }
516  if (input_type->Is(Type::OrderedNumber())) {
517    // JSToBoolean(x:ordered-number) => BooleanNot(NumberEqual(x, #0))
518    Node* cmp = graph()->NewNode(simplified()->NumberEqual(), input,
519                                 jsgraph()->ZeroConstant());
520    Node* inv = graph()->NewNode(simplified()->BooleanNot(), cmp);
521    return ReplaceWith(inv);
522  }
523  // TODO(turbofan): js-typed-lowering of ToBoolean(string)
524  return NoChange();
525}
526
527
528Reduction JSTypedLowering::ReduceJSLoadProperty(Node* node) {
529  Node* key = NodeProperties::GetValueInput(node, 1);
530  Node* base = NodeProperties::GetValueInput(node, 0);
531  Type* key_type = NodeProperties::GetBounds(key).upper;
532  Type* base_type = NodeProperties::GetBounds(base).upper;
533  // TODO(mstarzinger): This lowering is not correct if:
534  //   a) The typed array turns external (i.e. MaterializeArrayBuffer)
535  //   b) The typed array or it's buffer is neutered.
536  //   c) The index is out of bounds.
537  if (base_type->IsConstant() && key_type->Is(Type::Integral32()) &&
538      base_type->AsConstant()->Value()->IsJSTypedArray()) {
539    // JSLoadProperty(typed-array, int32)
540    JSTypedArray* array = JSTypedArray::cast(*base_type->AsConstant()->Value());
541    ElementsKind elements_kind = array->map()->elements_kind();
542    ExternalArrayType type = array->type();
543    uint32_t length;
544    CHECK(array->length()->ToUint32(&length));
545    ElementAccess element_access;
546    Node* elements = graph()->NewNode(
547        simplified()->LoadField(AccessBuilder::ForJSObjectElements()), base,
548        NodeProperties::GetEffectInput(node));
549    if (IsExternalArrayElementsKind(elements_kind)) {
550      elements = graph()->NewNode(
551          simplified()->LoadField(AccessBuilder::ForExternalArrayPointer()),
552          elements, NodeProperties::GetEffectInput(node));
553      element_access = AccessBuilder::ForTypedArrayElement(type, true);
554    } else {
555      DCHECK(IsFixedTypedArrayElementsKind(elements_kind));
556      element_access = AccessBuilder::ForTypedArrayElement(type, false);
557    }
558    Node* value =
559        graph()->NewNode(simplified()->LoadElement(element_access), elements,
560                         key, jsgraph()->Uint32Constant(length),
561                         NodeProperties::GetEffectInput(node));
562    return ReplaceEagerly(node, value);
563  }
564  return NoChange();
565}
566
567
568Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) {
569  Node* key = NodeProperties::GetValueInput(node, 1);
570  Node* base = NodeProperties::GetValueInput(node, 0);
571  Node* value = NodeProperties::GetValueInput(node, 2);
572  Type* key_type = NodeProperties::GetBounds(key).upper;
573  Type* base_type = NodeProperties::GetBounds(base).upper;
574  // TODO(mstarzinger): This lowering is not correct if:
575  //   a) The typed array turns external (i.e. MaterializeArrayBuffer)
576  //   b) The typed array or it's buffer is neutered.
577  if (key_type->Is(Type::Integral32()) && base_type->IsConstant() &&
578      base_type->AsConstant()->Value()->IsJSTypedArray()) {
579    // JSStoreProperty(typed-array, int32, value)
580    JSTypedArray* array = JSTypedArray::cast(*base_type->AsConstant()->Value());
581    ElementsKind elements_kind = array->map()->elements_kind();
582    ExternalArrayType type = array->type();
583    uint32_t length;
584    CHECK(array->length()->ToUint32(&length));
585    ElementAccess element_access;
586    Node* elements = graph()->NewNode(
587        simplified()->LoadField(AccessBuilder::ForJSObjectElements()), base,
588        NodeProperties::GetEffectInput(node));
589    if (IsExternalArrayElementsKind(elements_kind)) {
590      elements = graph()->NewNode(
591          simplified()->LoadField(AccessBuilder::ForExternalArrayPointer()),
592          elements, NodeProperties::GetEffectInput(node));
593      element_access = AccessBuilder::ForTypedArrayElement(type, true);
594    } else {
595      DCHECK(IsFixedTypedArrayElementsKind(elements_kind));
596      element_access = AccessBuilder::ForTypedArrayElement(type, false);
597    }
598
599    Node* check = graph()->NewNode(machine()->Uint32LessThan(), key,
600                                   jsgraph()->Uint32Constant(length));
601    Node* branch = graph()->NewNode(common()->Branch(), check,
602                                    NodeProperties::GetControlInput(node));
603
604    Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
605    Node* store =
606        graph()->NewNode(simplified()->StoreElement(element_access), elements,
607                         key, jsgraph()->Uint32Constant(length), value,
608                         NodeProperties::GetEffectInput(node), if_true);
609
610    Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
611
612    Node* merge = graph()->NewNode(common()->Merge(2), if_true, if_false);
613    Node* phi = graph()->NewNode(common()->EffectPhi(2), store,
614                                 NodeProperties::GetEffectInput(node), merge);
615
616    return ReplaceWith(phi);
617  }
618  return NoChange();
619}
620
621
622static Reduction ReplaceWithReduction(Node* node, Reduction reduction) {
623  if (reduction.Changed()) {
624    NodeProperties::ReplaceWithValue(node, reduction.replacement());
625    return reduction;
626  }
627  return Reducer::NoChange();
628}
629
630
631Reduction JSTypedLowering::Reduce(Node* node) {
632  switch (node->opcode()) {
633    case IrOpcode::kJSEqual:
634      return ReduceJSEqual(node, false);
635    case IrOpcode::kJSNotEqual:
636      return ReduceJSEqual(node, true);
637    case IrOpcode::kJSStrictEqual:
638      return ReduceJSStrictEqual(node, false);
639    case IrOpcode::kJSStrictNotEqual:
640      return ReduceJSStrictEqual(node, true);
641    case IrOpcode::kJSLessThan:         // fall through
642    case IrOpcode::kJSGreaterThan:      // fall through
643    case IrOpcode::kJSLessThanOrEqual:  // fall through
644    case IrOpcode::kJSGreaterThanOrEqual:
645      return ReduceJSComparison(node);
646    case IrOpcode::kJSBitwiseOr:
647      return ReduceI32Binop(node, true, true, machine()->Word32Or());
648    case IrOpcode::kJSBitwiseXor:
649      return ReduceI32Binop(node, true, true, machine()->Word32Xor());
650    case IrOpcode::kJSBitwiseAnd:
651      return ReduceI32Binop(node, true, true, machine()->Word32And());
652    case IrOpcode::kJSShiftLeft:
653      return ReduceI32Shift(node, true, machine()->Word32Shl());
654    case IrOpcode::kJSShiftRight:
655      return ReduceI32Shift(node, true, machine()->Word32Sar());
656    case IrOpcode::kJSShiftRightLogical:
657      return ReduceI32Shift(node, false, machine()->Word32Shr());
658    case IrOpcode::kJSAdd:
659      return ReduceJSAdd(node);
660    case IrOpcode::kJSSubtract:
661      return ReduceNumberBinop(node, simplified()->NumberSubtract());
662    case IrOpcode::kJSMultiply:
663      return ReduceNumberBinop(node, simplified()->NumberMultiply());
664    case IrOpcode::kJSDivide:
665      return ReduceNumberBinop(node, simplified()->NumberDivide());
666    case IrOpcode::kJSModulus:
667      return ReduceNumberBinop(node, simplified()->NumberModulus());
668    case IrOpcode::kJSUnaryNot: {
669      Reduction result = ReduceJSToBooleanInput(node->InputAt(0));
670      Node* value;
671      if (result.Changed()) {
672        // JSUnaryNot(x:boolean) => BooleanNot(x)
673        value =
674            graph()->NewNode(simplified()->BooleanNot(), result.replacement());
675        NodeProperties::ReplaceWithValue(node, value);
676        return Changed(value);
677      } else {
678        // JSUnaryNot(x) => BooleanNot(JSToBoolean(x))
679        value = graph()->NewNode(simplified()->BooleanNot(), node);
680        node->set_op(javascript()->ToBoolean());
681        NodeProperties::ReplaceWithValue(node, value, node);
682        // Note: ReplaceUses() smashes all uses, so smash it back here.
683        value->ReplaceInput(0, node);
684        return Changed(node);
685      }
686    }
687    case IrOpcode::kJSToBoolean:
688      return ReplaceWithReduction(node,
689                                  ReduceJSToBooleanInput(node->InputAt(0)));
690    case IrOpcode::kJSToNumber:
691      return ReplaceWithReduction(node,
692                                  ReduceJSToNumberInput(node->InputAt(0)));
693    case IrOpcode::kJSToString:
694      return ReplaceWithReduction(node,
695                                  ReduceJSToStringInput(node->InputAt(0)));
696    case IrOpcode::kJSLoadProperty:
697      return ReduceJSLoadProperty(node);
698    case IrOpcode::kJSStoreProperty:
699      return ReduceJSStoreProperty(node);
700    case IrOpcode::kJSCallFunction:
701      return JSBuiltinReducer(jsgraph()).Reduce(node);
702    default:
703      break;
704  }
705  return NoChange();
706}
707
708}  // namespace compiler
709}  // namespace internal
710}  // namespace v8
711