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 
13 namespace v8 {
14 namespace internal {
15 namespace 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?
RelaxEffects(Node * node)27 static void RelaxEffects(Node* node) {
28   NodeProperties::ReplaceWithValue(node, node, NULL);
29 }
30 
31 
~JSTypedLowering()32 JSTypedLowering::~JSTypedLowering() {}
33 
34 
ReplaceEagerly(Node * old,Node * node)35 Reduction 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.
45 class JSBinopReduction {
46  public:
JSBinopReduction(JSTypedLowering * lowering,Node * node)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 
ConvertInputsToNumber()53   void ConvertInputsToNumber() {
54     node_->ReplaceInput(0, ConvertToNumber(left()));
55     node_->ReplaceInput(1, ConvertToNumber(right()));
56   }
57 
ConvertInputsToInt32(bool left_signed,bool right_signed)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 
ConvertInputsToString()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).
ConvertInputsForShift(bool left_signed)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 
SwapInputs()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.
ChangeToPureOperator(const Operator * op,bool invert=false)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 
OneInputIs(Type * t)112   bool OneInputIs(Type* t) { return left_type_->Is(t) || right_type_->Is(t); }
113 
BothInputsAre(Type * t)114   bool BothInputsAre(Type* t) {
115     return left_type_->Is(t) && right_type_->Is(t);
116   }
117 
OneInputCannotBe(Type * t)118   bool OneInputCannotBe(Type* t) {
119     return !left_type_->Maybe(t) || !right_type_->Maybe(t);
120   }
121 
NeitherInputCanBe(Type * t)122   bool NeitherInputCanBe(Type* t) {
123     return !left_type_->Maybe(t) && !right_type_->Maybe(t);
124   }
125 
effect()126   Node* effect() { return NodeProperties::GetEffectInput(node_); }
control()127   Node* control() { return NodeProperties::GetControlInput(node_); }
context()128   Node* context() { return NodeProperties::GetContextInput(node_); }
left()129   Node* left() { return NodeProperties::GetValueInput(node_, 0); }
right()130   Node* right() { return NodeProperties::GetValueInput(node_, 1); }
left_type()131   Type* left_type() { return left_type_; }
right_type()132   Type* right_type() { return right_type_; }
133 
simplified()134   SimplifiedOperatorBuilder* simplified() { return lowering_->simplified(); }
graph()135   Graph* graph() { return lowering_->graph(); }
jsgraph()136   JSGraph* jsgraph() { return lowering_->jsgraph(); }
javascript()137   JSOperatorBuilder* javascript() { return lowering_->javascript(); }
machine()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 
ConvertToString(Node * node)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 
ConvertToNumber(Node * node)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.
TryNarrowingToI32(Type * type,Node * node)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 
ConvertToI32(bool is_signed,Node * node)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 
update_effect(Node * effect)218   void update_effect(Node* effect) {
219     NodeProperties::ReplaceEffectInput(node_, effect);
220   }
221 };
222 
223 
ReduceJSAdd(Node * node)224 Reduction 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 
ReduceNumberBinop(Node * node,const Operator * numberOp)253 Reduction 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 
ReduceI32Binop(Node * node,bool left_signed,bool right_signed,const Operator * intOp)268 Reduction 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 
ReduceI32Shift(Node * node,bool left_signed,const Operator * shift_op)281 Reduction 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 
ReduceJSComparison(Node * node)289 Reduction 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 
ReduceJSEqual(Node * node,bool invert)357 Reduction 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 
ReduceJSStrictEqual(Node * node,bool invert)377 Reduction 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 
ReduceJSToNumberInput(Node * input)425 Reduction 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 
ReduceJSToStringInput(Node * input)458 Reduction 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 
ReduceJSToBooleanInput(Node * input)486 Reduction 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 
ReduceJSLoadProperty(Node * node)528 Reduction 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 
ReduceJSStoreProperty(Node * node)568 Reduction 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 
ReplaceWithReduction(Node * node,Reduction reduction)622 static 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 
Reduce(Node * node)631 Reduction 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