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