| OLD | NEW |
| 1 // Copyright 2014 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/compiler/access-builder.h" | 5 #include "src/compiler/access-builder.h" |
| 6 #include "src/compiler/graph-inl.h" | 6 #include "src/compiler/graph-inl.h" |
| 7 #include "src/compiler/js-graph.h" | 7 #include "src/compiler/js-graph.h" |
| 8 #include "src/compiler/js-typed-lowering.h" | 8 #include "src/compiler/js-typed-lowering.h" |
| 9 #include "src/compiler/node-aux-data-inl.h" | 9 #include "src/compiler/node-aux-data-inl.h" |
| 10 #include "src/compiler/node-matchers.h" | 10 #include "src/compiler/node-matchers.h" |
| (...skipping 472 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 483 return r.ChangeToPureOperator(simplified()->StringEqual(), invert); | 483 return r.ChangeToPureOperator(simplified()->StringEqual(), invert); |
| 484 } | 484 } |
| 485 if (r.BothInputsAre(Type::Number())) { | 485 if (r.BothInputsAre(Type::Number())) { |
| 486 return r.ChangeToPureOperator(simplified()->NumberEqual(), invert); | 486 return r.ChangeToPureOperator(simplified()->NumberEqual(), invert); |
| 487 } | 487 } |
| 488 // TODO(turbofan): js-typed-lowering of StrictEqual(mixed types) | 488 // TODO(turbofan): js-typed-lowering of StrictEqual(mixed types) |
| 489 return NoChange(); | 489 return NoChange(); |
| 490 } | 490 } |
| 491 | 491 |
| 492 | 492 |
| 493 Reduction JSTypedLowering::ReduceJSToBooleanInput(Node* input) { | 493 Reduction JSTypedLowering::ReduceJSUnaryNot(Node* node) { |
| 494 if (input->opcode() == IrOpcode::kJSToBoolean) { | 494 Node* input = node->InputAt(0); |
| 495 // Recursively try to reduce the input first. | |
| 496 Reduction result = ReduceJSToBoolean(input); | |
| 497 if (result.Changed()) return result; | |
| 498 return Changed(input); // JSToBoolean(JSToBoolean(x)) => JSToBoolean(x) | |
| 499 } | |
| 500 // Check if we have a cached conversion. | |
| 501 Node* conversion = FindConversion<IrOpcode::kJSToBoolean>(input); | |
| 502 if (conversion) return Replace(conversion); | |
| 503 Type* input_type = NodeProperties::GetBounds(input).upper; | 495 Type* input_type = NodeProperties::GetBounds(input).upper; |
| 504 if (input_type->Is(Type::Boolean())) { | 496 if (input_type->Is(Type::Boolean())) { |
| 505 return Changed(input); // JSToBoolean(x:boolean) => x | 497 // JSUnaryNot(x:boolean,context) => BooleanNot(x) |
| 498 node->set_op(simplified()->BooleanNot()); |
| 499 node->TrimInputCount(1); |
| 500 return Changed(node); |
| 506 } | 501 } |
| 507 if (input_type->Is(Type::Undefined())) { | 502 // JSUnaryNot(x,context) => BooleanNot(AnyToBoolean(x)) |
| 508 // JSToBoolean(undefined) => #false | 503 node->set_op(simplified()->BooleanNot()); |
| 509 return Replace(jsgraph()->FalseConstant()); | 504 node->ReplaceInput(0, graph()->NewNode(simplified()->AnyToBoolean(), input)); |
| 510 } | 505 node->TrimInputCount(1); |
| 511 if (input_type->Is(Type::Null())) { | 506 return Changed(node); |
| 512 // JSToBoolean(null) => #false | |
| 513 return Replace(jsgraph()->FalseConstant()); | |
| 514 } | |
| 515 if (input_type->Is(Type::DetectableReceiver())) { | |
| 516 // JSToBoolean(x:detectable) => #true | |
| 517 return Replace(jsgraph()->TrueConstant()); | |
| 518 } | |
| 519 if (input_type->Is(Type::Undetectable())) { | |
| 520 // JSToBoolean(x:undetectable) => #false | |
| 521 return Replace(jsgraph()->FalseConstant()); | |
| 522 } | |
| 523 if (input_type->Is(Type::OrderedNumber())) { | |
| 524 // JSToBoolean(x:ordered-number) => BooleanNot(NumberEqual(x, #0)) | |
| 525 Node* cmp = graph()->NewNode(simplified()->NumberEqual(), input, | |
| 526 jsgraph()->ZeroConstant()); | |
| 527 Node* inv = graph()->NewNode(simplified()->BooleanNot(), cmp); | |
| 528 return Replace(inv); | |
| 529 } | |
| 530 if (input_type->Is(Type::String())) { | |
| 531 // JSToBoolean(x:string) => BooleanNot(NumberEqual(x.length, #0)) | |
| 532 FieldAccess access = AccessBuilder::ForStringLength(); | |
| 533 Node* length = graph()->NewNode(simplified()->LoadField(access), input, | |
| 534 graph()->start(), graph()->start()); | |
| 535 Node* cmp = graph()->NewNode(simplified()->NumberEqual(), length, | |
| 536 jsgraph()->ZeroConstant()); | |
| 537 Node* inv = graph()->NewNode(simplified()->BooleanNot(), cmp); | |
| 538 return Replace(inv); | |
| 539 } | |
| 540 return NoChange(); | |
| 541 } | 507 } |
| 542 | 508 |
| 543 | 509 |
| 544 Reduction JSTypedLowering::ReduceJSToBoolean(Node* node) { | 510 Reduction JSTypedLowering::ReduceJSToBoolean(Node* node) { |
| 545 // Try to reduce the input first. | 511 Node* input = node->InputAt(0); |
| 546 Node* const input = node->InputAt(0); | 512 Type* input_type = NodeProperties::GetBounds(input).upper; |
| 547 Reduction reduction = ReduceJSToBooleanInput(input); | 513 if (input_type->Is(Type::Boolean())) { |
| 548 if (reduction.Changed()) return reduction; | 514 // JSToBoolean(x:boolean,context) => x |
| 549 if (input->opcode() == IrOpcode::kPhi) { | 515 return Replace(input); |
| 550 // JSToBoolean(phi(x1,...,xn,control),context) | |
| 551 // => phi(JSToBoolean(x1,no-context),...,JSToBoolean(xn,no-context)) | |
| 552 int const input_count = input->InputCount() - 1; | |
| 553 Node* const control = input->InputAt(input_count); | |
| 554 DCHECK_LE(0, input_count); | |
| 555 DCHECK(NodeProperties::IsControl(control)); | |
| 556 DCHECK(NodeProperties::GetBounds(node).upper->Is(Type::Boolean())); | |
| 557 DCHECK(!NodeProperties::GetBounds(input).upper->Is(Type::Boolean())); | |
| 558 node->set_op(common()->Phi(kMachAnyTagged, input_count)); | |
| 559 for (int i = 0; i < input_count; ++i) { | |
| 560 // We must be very careful not to introduce cycles when pushing | |
| 561 // operations into phis. It is safe for {value}, since it appears | |
| 562 // as input to the phi that we are replacing, but it's not safe | |
| 563 // to simply reuse the context of the {node}. However, ToBoolean() | |
| 564 // does not require a context anyways, so it's safe to discard it | |
| 565 // here and pass the dummy context. | |
| 566 Node* const value = ConvertToBoolean(input->InputAt(i)); | |
| 567 if (i < node->InputCount()) { | |
| 568 node->ReplaceInput(i, value); | |
| 569 } else { | |
| 570 node->AppendInput(graph()->zone(), value); | |
| 571 } | |
| 572 } | |
| 573 if (input_count < node->InputCount()) { | |
| 574 node->ReplaceInput(input_count, control); | |
| 575 } else { | |
| 576 node->AppendInput(graph()->zone(), control); | |
| 577 } | |
| 578 node->TrimInputCount(input_count + 1); | |
| 579 return Changed(node); | |
| 580 } | 516 } |
| 581 if (input->opcode() == IrOpcode::kSelect) { | 517 // JSToBoolean(x,context) => AnyToBoolean(x) |
| 582 // JSToBoolean(select(c,x1,x2),context) | 518 node->set_op(simplified()->AnyToBoolean()); |
| 583 // => select(c,JSToBoolean(x1,no-context),...,JSToBoolean(x2,no-context)) | 519 node->TrimInputCount(1); |
| 584 int const input_count = input->InputCount(); | 520 return Changed(node); |
| 585 BranchHint const input_hint = SelectParametersOf(input->op()).hint(); | |
| 586 DCHECK_EQ(3, input_count); | |
| 587 DCHECK(NodeProperties::GetBounds(node).upper->Is(Type::Boolean())); | |
| 588 DCHECK(!NodeProperties::GetBounds(input).upper->Is(Type::Boolean())); | |
| 589 node->set_op(common()->Select(kMachAnyTagged, input_hint)); | |
| 590 node->InsertInput(graph()->zone(), 0, input->InputAt(0)); | |
| 591 for (int i = 1; i < input_count; ++i) { | |
| 592 // We must be very careful not to introduce cycles when pushing | |
| 593 // operations into selects. It is safe for {value}, since it appears | |
| 594 // as input to the select that we are replacing, but it's not safe | |
| 595 // to simply reuse the context of the {node}. However, ToBoolean() | |
| 596 // does not require a context anyways, so it's safe to discard it | |
| 597 // here and pass the dummy context. | |
| 598 Node* const value = ConvertToBoolean(input->InputAt(i)); | |
| 599 node->ReplaceInput(i, value); | |
| 600 } | |
| 601 DCHECK_EQ(3, node->InputCount()); | |
| 602 return Changed(node); | |
| 603 } | |
| 604 InsertConversion(node); | |
| 605 if (node->InputAt(1) != jsgraph()->NoContextConstant()) { | |
| 606 // JSToBoolean(x,context) => JSToBoolean(x,no-context) | |
| 607 node->ReplaceInput(1, jsgraph()->NoContextConstant()); | |
| 608 return Changed(node); | |
| 609 } | |
| 610 return NoChange(); | |
| 611 } | 521 } |
| 612 | 522 |
| 613 | 523 |
| 614 Reduction JSTypedLowering::ReduceJSToNumberInput(Node* input) { | 524 Reduction JSTypedLowering::ReduceJSToNumberInput(Node* input) { |
| 615 if (input->opcode() == IrOpcode::kJSToNumber) { | 525 if (input->opcode() == IrOpcode::kJSToNumber) { |
| 616 // Recursively try to reduce the input first. | 526 // Recursively try to reduce the input first. |
| 617 Reduction result = ReduceJSToNumber(input); | 527 Reduction result = ReduceJSToNumber(input); |
| 618 if (result.Changed()) return result; | 528 if (result.Changed()) return result; |
| 619 return Changed(input); // JSToNumber(JSToNumber(x)) => JSToNumber(x) | 529 return Changed(input); // JSToNumber(JSToNumber(x)) => JSToNumber(x) |
| 620 } | 530 } |
| (...skipping 344 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 965 case IrOpcode::kJSAdd: | 875 case IrOpcode::kJSAdd: |
| 966 return ReduceJSAdd(node); | 876 return ReduceJSAdd(node); |
| 967 case IrOpcode::kJSSubtract: | 877 case IrOpcode::kJSSubtract: |
| 968 return ReduceNumberBinop(node, simplified()->NumberSubtract()); | 878 return ReduceNumberBinop(node, simplified()->NumberSubtract()); |
| 969 case IrOpcode::kJSMultiply: | 879 case IrOpcode::kJSMultiply: |
| 970 return ReduceJSMultiply(node); | 880 return ReduceJSMultiply(node); |
| 971 case IrOpcode::kJSDivide: | 881 case IrOpcode::kJSDivide: |
| 972 return ReduceNumberBinop(node, simplified()->NumberDivide()); | 882 return ReduceNumberBinop(node, simplified()->NumberDivide()); |
| 973 case IrOpcode::kJSModulus: | 883 case IrOpcode::kJSModulus: |
| 974 return ReduceNumberBinop(node, simplified()->NumberModulus()); | 884 return ReduceNumberBinop(node, simplified()->NumberModulus()); |
| 975 case IrOpcode::kJSUnaryNot: { | 885 case IrOpcode::kJSUnaryNot: |
| 976 Reduction result = ReduceJSToBooleanInput(node->InputAt(0)); | 886 return ReduceJSUnaryNot(node); |
| 977 if (result.Changed()) { | |
| 978 // JSUnaryNot(x:boolean) => BooleanNot(x) | |
| 979 node = result.replacement(); | |
| 980 } else { | |
| 981 // JSUnaryNot(x) => BooleanNot(JSToBoolean(x)) | |
| 982 node->set_op(javascript()->ToBoolean()); | |
| 983 } | |
| 984 Node* value = graph()->NewNode(simplified()->BooleanNot(), node); | |
| 985 return Replace(value); | |
| 986 } | |
| 987 case IrOpcode::kJSToBoolean: | 887 case IrOpcode::kJSToBoolean: |
| 988 return ReduceJSToBoolean(node); | 888 return ReduceJSToBoolean(node); |
| 989 case IrOpcode::kJSToNumber: | 889 case IrOpcode::kJSToNumber: |
| 990 return ReduceJSToNumber(node); | 890 return ReduceJSToNumber(node); |
| 991 case IrOpcode::kJSToString: | 891 case IrOpcode::kJSToString: |
| 992 return ReduceJSToString(node); | 892 return ReduceJSToString(node); |
| 993 case IrOpcode::kJSLoadProperty: | 893 case IrOpcode::kJSLoadProperty: |
| 994 return ReduceJSLoadProperty(node); | 894 return ReduceJSLoadProperty(node); |
| 995 case IrOpcode::kJSStoreProperty: | 895 case IrOpcode::kJSStoreProperty: |
| 996 return ReduceJSStoreProperty(node); | 896 return ReduceJSStoreProperty(node); |
| 997 case IrOpcode::kJSLoadContext: | 897 case IrOpcode::kJSLoadContext: |
| 998 return ReduceJSLoadContext(node); | 898 return ReduceJSLoadContext(node); |
| 999 case IrOpcode::kJSStoreContext: | 899 case IrOpcode::kJSStoreContext: |
| 1000 return ReduceJSStoreContext(node); | 900 return ReduceJSStoreContext(node); |
| 1001 default: | 901 default: |
| 1002 break; | 902 break; |
| 1003 } | 903 } |
| 1004 return NoChange(); | 904 return NoChange(); |
| 1005 } | 905 } |
| 1006 | 906 |
| 1007 | 907 |
| 1008 Node* JSTypedLowering::ConvertToBoolean(Node* input) { | |
| 1009 // Avoid inserting too many eager ToBoolean() operations. | |
| 1010 Reduction const reduction = ReduceJSToBooleanInput(input); | |
| 1011 if (reduction.Changed()) return reduction.replacement(); | |
| 1012 Node* const conversion = graph()->NewNode(javascript()->ToBoolean(), input, | |
| 1013 jsgraph()->NoContextConstant()); | |
| 1014 InsertConversion(conversion); | |
| 1015 return conversion; | |
| 1016 } | |
| 1017 | |
| 1018 | |
| 1019 Node* JSTypedLowering::ConvertToNumber(Node* input) { | 908 Node* JSTypedLowering::ConvertToNumber(Node* input) { |
| 1020 DCHECK(NodeProperties::GetBounds(input).upper->Is(Type::PlainPrimitive())); | 909 DCHECK(NodeProperties::GetBounds(input).upper->Is(Type::PlainPrimitive())); |
| 1021 // Avoid inserting too many eager ToNumber() operations. | 910 // Avoid inserting too many eager ToNumber() operations. |
| 1022 Reduction const reduction = ReduceJSToNumberInput(input); | 911 Reduction const reduction = ReduceJSToNumberInput(input); |
| 1023 if (reduction.Changed()) return reduction.replacement(); | 912 if (reduction.Changed()) return reduction.replacement(); |
| 1024 Node* const conversion = graph()->NewNode(javascript()->ToNumber(), input, | 913 Node* const conversion = graph()->NewNode(javascript()->ToNumber(), input, |
| 1025 jsgraph()->NoContextConstant(), | 914 jsgraph()->NoContextConstant(), |
| 1026 graph()->start(), graph()->start()); | 915 graph()->start(), graph()->start()); |
| 1027 InsertConversion(conversion); | 916 InsertConversion(conversion); |
| 1028 return conversion; | 917 return conversion; |
| 1029 } | 918 } |
| 1030 | 919 |
| 1031 | 920 |
| 1032 template <IrOpcode::Value kOpcode> | 921 template <IrOpcode::Value kOpcode> |
| 1033 Node* JSTypedLowering::FindConversion(Node* input) { | 922 Node* JSTypedLowering::FindConversion(Node* input) { |
| 1034 size_t const input_id = input->id(); | 923 size_t const input_id = input->id(); |
| 1035 if (input_id < conversions_.size()) { | 924 if (input_id < conversions_.size()) { |
| 1036 Node* const conversion = conversions_[input_id]; | 925 Node* const conversion = conversions_[input_id]; |
| 1037 if (conversion && conversion->opcode() == kOpcode) { | 926 if (conversion && conversion->opcode() == kOpcode) { |
| 1038 return conversion; | 927 return conversion; |
| 1039 } | 928 } |
| 1040 } | 929 } |
| 1041 return nullptr; | 930 return nullptr; |
| 1042 } | 931 } |
| 1043 | 932 |
| 1044 | 933 |
| 1045 void JSTypedLowering::InsertConversion(Node* conversion) { | 934 void JSTypedLowering::InsertConversion(Node* conversion) { |
| 1046 DCHECK(conversion->opcode() == IrOpcode::kJSToBoolean || | 935 DCHECK(conversion->opcode() == IrOpcode::kJSToNumber); |
| 1047 conversion->opcode() == IrOpcode::kJSToNumber); | |
| 1048 size_t const input_id = conversion->InputAt(0)->id(); | 936 size_t const input_id = conversion->InputAt(0)->id(); |
| 1049 if (input_id >= conversions_.size()) { | 937 if (input_id >= conversions_.size()) { |
| 1050 conversions_.resize(2 * input_id + 1); | 938 conversions_.resize(2 * input_id + 1); |
| 1051 } | 939 } |
| 1052 conversions_[input_id] = conversion; | 940 conversions_[input_id] = conversion; |
| 1053 } | 941 } |
| 1054 | 942 |
| 1055 | 943 |
| 1056 Node* JSTypedLowering::Word32Shl(Node* const lhs, int32_t const rhs) { | 944 Node* JSTypedLowering::Word32Shl(Node* const lhs, int32_t const rhs) { |
| 1057 if (rhs == 0) return lhs; | 945 if (rhs == 0) return lhs; |
| (...skipping 18 matching lines...) Expand all Loading... |
| 1076 } | 964 } |
| 1077 | 965 |
| 1078 | 966 |
| 1079 MachineOperatorBuilder* JSTypedLowering::machine() const { | 967 MachineOperatorBuilder* JSTypedLowering::machine() const { |
| 1080 return jsgraph()->machine(); | 968 return jsgraph()->machine(); |
| 1081 } | 969 } |
| 1082 | 970 |
| 1083 } // namespace compiler | 971 } // namespace compiler |
| 1084 } // namespace internal | 972 } // namespace internal |
| 1085 } // namespace v8 | 973 } // namespace v8 |
| OLD | NEW |