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/base/bits.h" | 5 #include "src/base/bits.h" |
6 #include "src/compiler/instruction-selector-impl.h" | 6 #include "src/compiler/instruction-selector-impl.h" |
7 #include "src/compiler/node-matchers.h" | 7 #include "src/compiler/node-matchers.h" |
8 | 8 |
9 namespace v8 { | 9 namespace v8 { |
10 namespace internal { | 10 namespace internal { |
(...skipping 774 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
785 g.UseFixed(node->InputAt(1), d1))->MarkAsCall(); | 785 g.UseFixed(node->InputAt(1), d1))->MarkAsCall(); |
786 } | 786 } |
787 | 787 |
788 | 788 |
789 void InstructionSelector::VisitFloat64Sqrt(Node* node) { | 789 void InstructionSelector::VisitFloat64Sqrt(Node* node) { |
790 ArmOperandGenerator g(this); | 790 ArmOperandGenerator g(this); |
791 Emit(kArmVsqrtF64, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0))); | 791 Emit(kArmVsqrtF64, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0))); |
792 } | 792 } |
793 | 793 |
794 | 794 |
795 void InstructionSelector::VisitCall(Node* call, BasicBlock* continuation, | 795 void InstructionSelector::VisitCall(Node* node) { |
796 BasicBlock* deoptimization) { | |
797 ArmOperandGenerator g(this); | 796 ArmOperandGenerator g(this); |
798 CallDescriptor* descriptor = OpParameter<CallDescriptor*>(call); | 797 CallDescriptor* descriptor = OpParameter<CallDescriptor*>(node); |
799 | 798 |
800 FrameStateDescriptor* frame_state_descriptor = NULL; | 799 FrameStateDescriptor* frame_state_descriptor = NULL; |
801 if (descriptor->NeedsFrameState()) { | 800 if (descriptor->NeedsFrameState()) { |
802 frame_state_descriptor = | 801 frame_state_descriptor = |
803 GetFrameStateDescriptor(call->InputAt(descriptor->InputCount())); | 802 GetFrameStateDescriptor(node->InputAt(descriptor->InputCount())); |
804 } | 803 } |
805 | 804 |
806 CallBuffer buffer(zone(), descriptor, frame_state_descriptor); | 805 CallBuffer buffer(zone(), descriptor, frame_state_descriptor); |
807 | 806 |
808 // Compute InstructionOperands for inputs and outputs. | 807 // Compute InstructionOperands for inputs and outputs. |
809 // TODO(turbofan): on ARM64 it's probably better to use the code object in a | 808 // TODO(turbofan): on ARM64 it's probably better to use the code object in a |
810 // register if there are multiple uses of it. Improve constant pool and the | 809 // register if there are multiple uses of it. Improve constant pool and the |
811 // heuristics in the register allocator for where to emit constants. | 810 // heuristics in the register allocator for where to emit constants. |
812 InitializeCallBuffer(call, &buffer, true, false); | 811 InitializeCallBuffer(node, &buffer, true, false); |
813 | 812 |
814 // TODO(dcarney): might be possible to use claim/poke instead | 813 // TODO(dcarney): might be possible to use claim/poke instead |
815 // Push any stack arguments. | 814 // Push any stack arguments. |
816 for (NodeVectorRIter input = buffer.pushed_nodes.rbegin(); | 815 for (NodeVectorRIter input = buffer.pushed_nodes.rbegin(); |
817 input != buffer.pushed_nodes.rend(); input++) { | 816 input != buffer.pushed_nodes.rend(); input++) { |
818 Emit(kArmPush, NULL, g.UseRegister(*input)); | 817 Emit(kArmPush, NULL, g.UseRegister(*input)); |
819 } | 818 } |
820 | 819 |
821 // Select the appropriate opcode based on the call type. | 820 // Select the appropriate opcode based on the call type. |
822 InstructionCode opcode; | 821 InstructionCode opcode; |
823 switch (descriptor->kind()) { | 822 switch (descriptor->kind()) { |
824 case CallDescriptor::kCallCodeObject: { | 823 case CallDescriptor::kCallCodeObject: { |
825 opcode = kArchCallCodeObject; | 824 opcode = kArchCallCodeObject; |
826 break; | 825 break; |
827 } | 826 } |
828 case CallDescriptor::kCallJSFunction: | 827 case CallDescriptor::kCallJSFunction: |
829 opcode = kArchCallJSFunction; | 828 opcode = kArchCallJSFunction; |
830 break; | 829 break; |
831 default: | 830 default: |
832 UNREACHABLE(); | 831 UNREACHABLE(); |
833 return; | 832 return; |
834 } | 833 } |
835 opcode |= MiscField::encode(descriptor->flags()); | 834 opcode |= MiscField::encode(descriptor->flags()); |
836 | 835 |
837 // Emit the call instruction. | 836 // Emit the call instruction. |
838 Instruction* call_instr = | 837 Instruction* call_instr = |
839 Emit(opcode, buffer.outputs.size(), &buffer.outputs.front(), | 838 Emit(opcode, buffer.outputs.size(), &buffer.outputs.front(), |
840 buffer.instruction_args.size(), &buffer.instruction_args.front()); | 839 buffer.instruction_args.size(), &buffer.instruction_args.front()); |
| 840 call_instr->MarkAsCall(); |
| 841 } |
841 | 842 |
842 call_instr->MarkAsCall(); | 843 |
843 if (deoptimization != NULL) { | 844 namespace { |
844 DCHECK(continuation != NULL); | 845 |
845 call_instr->MarkAsControl(); | 846 // Shared routine for multiple float compare operations. |
| 847 void VisitFloat64Compare(InstructionSelector* selector, Node* node, |
| 848 FlagsContinuation* cont) { |
| 849 ArmOperandGenerator g(selector); |
| 850 Float64BinopMatcher m(node); |
| 851 if (cont->IsBranch()) { |
| 852 selector->Emit(cont->Encode(kArmVcmpF64), nullptr, |
| 853 g.UseRegister(m.left().node()), |
| 854 g.UseRegister(m.right().node()), g.Label(cont->true_block()), |
| 855 g.Label(cont->false_block()))->MarkAsControl(); |
| 856 } else { |
| 857 DCHECK(cont->IsSet()); |
| 858 selector->Emit( |
| 859 cont->Encode(kArmVcmpF64), g.DefineAsRegister(cont->result()), |
| 860 g.UseRegister(m.left().node()), g.UseRegister(m.right().node())); |
846 } | 861 } |
847 } | 862 } |
848 | 863 |
849 | 864 |
850 void InstructionSelector::VisitInt32AddWithOverflow(Node* node, | 865 // Shared routine for multiple word compare operations. |
851 FlagsContinuation* cont) { | 866 void VisitWordCompare(InstructionSelector* selector, Node* node, |
852 VisitBinop(this, node, kArmAdd, kArmAdd, cont); | 867 InstructionCode opcode, FlagsContinuation* cont, |
853 } | 868 bool commutative) { |
854 | |
855 | |
856 void InstructionSelector::VisitInt32SubWithOverflow(Node* node, | |
857 FlagsContinuation* cont) { | |
858 VisitBinop(this, node, kArmSub, kArmRsb, cont); | |
859 } | |
860 | |
861 | |
862 // Shared routine for multiple compare operations. | |
863 static void VisitWordCompare(InstructionSelector* selector, Node* node, | |
864 InstructionCode opcode, FlagsContinuation* cont, | |
865 bool commutative) { | |
866 ArmOperandGenerator g(selector); | 869 ArmOperandGenerator g(selector); |
867 Int32BinopMatcher m(node); | 870 Int32BinopMatcher m(node); |
868 InstructionOperand* inputs[5]; | 871 InstructionOperand* inputs[5]; |
869 size_t input_count = 0; | 872 size_t input_count = 0; |
870 InstructionOperand* outputs[1]; | 873 InstructionOperand* outputs[1]; |
871 size_t output_count = 0; | 874 size_t output_count = 0; |
872 | 875 |
873 if (TryMatchImmediateOrShift(selector, &opcode, m.right().node(), | 876 if (TryMatchImmediateOrShift(selector, &opcode, m.right().node(), |
874 &input_count, &inputs[1])) { | 877 &input_count, &inputs[1])) { |
875 inputs[0] = g.UseRegister(m.left().node()); | 878 inputs[0] = g.UseRegister(m.left().node()); |
(...skipping 20 matching lines...) Expand all Loading... |
896 DCHECK_NE(0, input_count); | 899 DCHECK_NE(0, input_count); |
897 DCHECK_GE(arraysize(inputs), input_count); | 900 DCHECK_GE(arraysize(inputs), input_count); |
898 DCHECK_GE(arraysize(outputs), output_count); | 901 DCHECK_GE(arraysize(outputs), output_count); |
899 | 902 |
900 Instruction* instr = selector->Emit(cont->Encode(opcode), output_count, | 903 Instruction* instr = selector->Emit(cont->Encode(opcode), output_count, |
901 outputs, input_count, inputs); | 904 outputs, input_count, inputs); |
902 if (cont->IsBranch()) instr->MarkAsControl(); | 905 if (cont->IsBranch()) instr->MarkAsControl(); |
903 } | 906 } |
904 | 907 |
905 | 908 |
906 void InstructionSelector::VisitWord32Test(Node* node, FlagsContinuation* cont) { | 909 void VisitWordCompare(InstructionSelector* selector, Node* node, |
907 switch (node->opcode()) { | 910 FlagsContinuation* cont) { |
908 case IrOpcode::kInt32Add: | 911 VisitWordCompare(selector, node, kArmCmp, cont, false); |
909 return VisitWordCompare(this, node, kArmCmn, cont, true); | 912 } |
910 case IrOpcode::kInt32Sub: | 913 |
911 return VisitWordCompare(this, node, kArmCmp, cont, false); | 914 |
912 case IrOpcode::kWord32And: | 915 void VisitWordTest(InstructionSelector* selector, Node* node, |
913 return VisitWordCompare(this, node, kArmTst, cont, true); | 916 FlagsContinuation* cont) { |
914 case IrOpcode::kWord32Or: | 917 ArmOperandGenerator g(selector); |
915 return VisitBinop(this, node, kArmOrr, kArmOrr, cont); | |
916 case IrOpcode::kWord32Xor: | |
917 return VisitWordCompare(this, node, kArmTeq, cont, true); | |
918 case IrOpcode::kWord32Sar: | |
919 return VisitShift(this, node, TryMatchASR, cont); | |
920 case IrOpcode::kWord32Shl: | |
921 return VisitShift(this, node, TryMatchLSL, cont); | |
922 case IrOpcode::kWord32Shr: | |
923 return VisitShift(this, node, TryMatchLSR, cont); | |
924 case IrOpcode::kWord32Ror: | |
925 return VisitShift(this, node, TryMatchROR, cont); | |
926 default: | |
927 break; | |
928 } | |
929 | |
930 ArmOperandGenerator g(this); | |
931 InstructionCode opcode = | 918 InstructionCode opcode = |
932 cont->Encode(kArmTst) | AddressingModeField::encode(kMode_Operand2_R); | 919 cont->Encode(kArmTst) | AddressingModeField::encode(kMode_Operand2_R); |
933 if (cont->IsBranch()) { | 920 if (cont->IsBranch()) { |
934 Emit(opcode, NULL, g.UseRegister(node), g.UseRegister(node), | 921 selector->Emit(opcode, nullptr, g.UseRegister(node), g.UseRegister(node), |
935 g.Label(cont->true_block()), | 922 g.Label(cont->true_block()), |
936 g.Label(cont->false_block()))->MarkAsControl(); | 923 g.Label(cont->false_block()))->MarkAsControl(); |
937 } else { | 924 } else { |
938 Emit(opcode, g.DefineAsRegister(cont->result()), g.UseRegister(node), | 925 selector->Emit(opcode, g.DefineAsRegister(cont->result()), |
939 g.UseRegister(node)); | 926 g.UseRegister(node), g.UseRegister(node)); |
940 } | 927 } |
941 } | 928 } |
942 | 929 |
943 | 930 } // namespace |
944 void InstructionSelector::VisitWord32Compare(Node* node, | 931 |
945 FlagsContinuation* cont) { | 932 |
946 VisitWordCompare(this, node, kArmCmp, cont, false); | 933 void InstructionSelector::VisitBranch(Node* branch, BasicBlock* tbranch, |
947 } | 934 BasicBlock* fbranch) { |
948 | |
949 | |
950 void InstructionSelector::VisitFloat64Compare(Node* node, | |
951 FlagsContinuation* cont) { | |
952 ArmOperandGenerator g(this); | 935 ArmOperandGenerator g(this); |
953 Float64BinopMatcher m(node); | 936 Node* user = branch; |
954 if (cont->IsBranch()) { | 937 Node* value = branch->InputAt(0); |
955 Emit(cont->Encode(kArmVcmpF64), NULL, g.UseRegister(m.left().node()), | 938 |
956 g.UseRegister(m.right().node()), g.Label(cont->true_block()), | 939 FlagsContinuation cont(kNotEqual, tbranch, fbranch); |
957 g.Label(cont->false_block()))->MarkAsControl(); | 940 |
958 } else { | 941 // If we can fall through to the true block, invert the branch. |
959 DCHECK(cont->IsSet()); | 942 if (IsNextInAssemblyOrder(tbranch)) { |
960 Emit(cont->Encode(kArmVcmpF64), g.DefineAsRegister(cont->result()), | 943 cont.Negate(); |
961 g.UseRegister(m.left().node()), g.UseRegister(m.right().node())); | 944 cont.SwapBlocks(); |
962 } | 945 } |
| 946 |
| 947 // Try to combine with comparisons against 0 by simply inverting the branch. |
| 948 while (CanCover(user, value) && value->opcode() == IrOpcode::kWord32Equal) { |
| 949 Int32BinopMatcher m(value); |
| 950 if (m.right().Is(0)) { |
| 951 user = value; |
| 952 value = m.left().node(); |
| 953 cont.Negate(); |
| 954 } else { |
| 955 break; |
| 956 } |
| 957 } |
| 958 |
| 959 // Try to combine the branch with a comparison. |
| 960 if (CanCover(user, value)) { |
| 961 switch (value->opcode()) { |
| 962 case IrOpcode::kWord32Equal: |
| 963 cont.OverwriteAndNegateIfEqual(kEqual); |
| 964 return VisitWordCompare(this, value, &cont); |
| 965 case IrOpcode::kInt32LessThan: |
| 966 cont.OverwriteAndNegateIfEqual(kSignedLessThan); |
| 967 return VisitWordCompare(this, value, &cont); |
| 968 case IrOpcode::kInt32LessThanOrEqual: |
| 969 cont.OverwriteAndNegateIfEqual(kSignedLessThanOrEqual); |
| 970 return VisitWordCompare(this, value, &cont); |
| 971 case IrOpcode::kUint32LessThan: |
| 972 cont.OverwriteAndNegateIfEqual(kUnsignedLessThan); |
| 973 return VisitWordCompare(this, value, &cont); |
| 974 case IrOpcode::kUint32LessThanOrEqual: |
| 975 cont.OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual); |
| 976 return VisitWordCompare(this, value, &cont); |
| 977 case IrOpcode::kFloat64Equal: |
| 978 cont.OverwriteAndNegateIfEqual(kUnorderedEqual); |
| 979 return VisitFloat64Compare(this, value, &cont); |
| 980 case IrOpcode::kFloat64LessThan: |
| 981 cont.OverwriteAndNegateIfEqual(kUnorderedLessThan); |
| 982 return VisitFloat64Compare(this, value, &cont); |
| 983 case IrOpcode::kFloat64LessThanOrEqual: |
| 984 cont.OverwriteAndNegateIfEqual(kUnorderedLessThanOrEqual); |
| 985 return VisitFloat64Compare(this, value, &cont); |
| 986 case IrOpcode::kProjection: |
| 987 // Check if this is the overflow output projection of an |
| 988 // <Operation>WithOverflow node. |
| 989 if (OpParameter<size_t>(value) == 1u) { |
| 990 // We cannot combine the <Operation>WithOverflow with this branch |
| 991 // unless the 0th projection (the use of the actual value of the |
| 992 // <Operation> is either NULL, which means there's no use of the |
| 993 // actual value, or was already defined, which means it is scheduled |
| 994 // *AFTER* this branch). |
| 995 Node* node = value->InputAt(0); |
| 996 Node* result = node->FindProjection(0); |
| 997 if (!result || IsDefined(result)) { |
| 998 switch (node->opcode()) { |
| 999 case IrOpcode::kInt32AddWithOverflow: |
| 1000 cont.OverwriteAndNegateIfEqual(kOverflow); |
| 1001 return VisitBinop(this, node, kArmAdd, kArmAdd, &cont); |
| 1002 case IrOpcode::kInt32SubWithOverflow: |
| 1003 cont.OverwriteAndNegateIfEqual(kOverflow); |
| 1004 return VisitBinop(this, node, kArmSub, kArmRsb, &cont); |
| 1005 default: |
| 1006 break; |
| 1007 } |
| 1008 } |
| 1009 } |
| 1010 break; |
| 1011 case IrOpcode::kInt32Add: |
| 1012 return VisitWordCompare(this, value, kArmCmn, &cont, true); |
| 1013 case IrOpcode::kInt32Sub: |
| 1014 return VisitWordCompare(this, value, kArmCmp, &cont, false); |
| 1015 case IrOpcode::kWord32And: |
| 1016 return VisitWordCompare(this, value, kArmTst, &cont, true); |
| 1017 case IrOpcode::kWord32Or: |
| 1018 return VisitBinop(this, value, kArmOrr, kArmOrr, &cont); |
| 1019 case IrOpcode::kWord32Xor: |
| 1020 return VisitWordCompare(this, value, kArmTeq, &cont, true); |
| 1021 case IrOpcode::kWord32Sar: |
| 1022 return VisitShift(this, value, TryMatchASR, &cont); |
| 1023 case IrOpcode::kWord32Shl: |
| 1024 return VisitShift(this, value, TryMatchLSL, &cont); |
| 1025 case IrOpcode::kWord32Shr: |
| 1026 return VisitShift(this, value, TryMatchLSR, &cont); |
| 1027 case IrOpcode::kWord32Ror: |
| 1028 return VisitShift(this, value, TryMatchROR, &cont); |
| 1029 default: |
| 1030 break; |
| 1031 } |
| 1032 } |
| 1033 |
| 1034 // Branch could not be combined with a compare, emit compare against 0. |
| 1035 return VisitWordTest(this, value, &cont); |
| 1036 } |
| 1037 |
| 1038 |
| 1039 void InstructionSelector::VisitWord32Equal(Node* const node) { |
| 1040 Node* const user = node; |
| 1041 FlagsContinuation cont(kEqual, node); |
| 1042 Int32BinopMatcher m(user); |
| 1043 if (m.right().Is(0)) { |
| 1044 Node* const value = m.left().node(); |
| 1045 if (CanCover(user, value)) { |
| 1046 switch (value->opcode()) { |
| 1047 case IrOpcode::kInt32Add: |
| 1048 return VisitWordCompare(this, value, kArmCmn, &cont, true); |
| 1049 case IrOpcode::kInt32Sub: |
| 1050 return VisitWordCompare(this, value, kArmCmp, &cont, false); |
| 1051 case IrOpcode::kWord32And: |
| 1052 return VisitWordCompare(this, value, kArmTst, &cont, true); |
| 1053 case IrOpcode::kWord32Or: |
| 1054 return VisitBinop(this, value, kArmOrr, kArmOrr, &cont); |
| 1055 case IrOpcode::kWord32Xor: |
| 1056 return VisitWordCompare(this, value, kArmTeq, &cont, true); |
| 1057 case IrOpcode::kWord32Sar: |
| 1058 return VisitShift(this, value, TryMatchASR, &cont); |
| 1059 case IrOpcode::kWord32Shl: |
| 1060 return VisitShift(this, value, TryMatchLSL, &cont); |
| 1061 case IrOpcode::kWord32Shr: |
| 1062 return VisitShift(this, value, TryMatchLSR, &cont); |
| 1063 case IrOpcode::kWord32Ror: |
| 1064 return VisitShift(this, value, TryMatchROR, &cont); |
| 1065 default: |
| 1066 break; |
| 1067 } |
| 1068 return VisitWordTest(this, value, &cont); |
| 1069 } |
| 1070 } |
| 1071 VisitWordCompare(this, node, &cont); |
| 1072 } |
| 1073 |
| 1074 |
| 1075 void InstructionSelector::VisitInt32LessThan(Node* node) { |
| 1076 FlagsContinuation cont(kSignedLessThan, node); |
| 1077 VisitWordCompare(this, node, &cont); |
| 1078 } |
| 1079 |
| 1080 |
| 1081 void InstructionSelector::VisitInt32LessThanOrEqual(Node* node) { |
| 1082 FlagsContinuation cont(kSignedLessThanOrEqual, node); |
| 1083 VisitWordCompare(this, node, &cont); |
| 1084 } |
| 1085 |
| 1086 |
| 1087 void InstructionSelector::VisitUint32LessThan(Node* node) { |
| 1088 FlagsContinuation cont(kUnsignedLessThan, node); |
| 1089 VisitWordCompare(this, node, &cont); |
| 1090 } |
| 1091 |
| 1092 |
| 1093 void InstructionSelector::VisitUint32LessThanOrEqual(Node* node) { |
| 1094 FlagsContinuation cont(kUnsignedLessThanOrEqual, node); |
| 1095 VisitWordCompare(this, node, &cont); |
| 1096 } |
| 1097 |
| 1098 |
| 1099 void InstructionSelector::VisitInt32AddWithOverflow(Node* node) { |
| 1100 if (Node* ovf = node->FindProjection(1)) { |
| 1101 FlagsContinuation cont(kOverflow, ovf); |
| 1102 return VisitBinop(this, node, kArmAdd, kArmAdd, &cont); |
| 1103 } |
| 1104 FlagsContinuation cont; |
| 1105 VisitBinop(this, node, kArmAdd, kArmAdd, &cont); |
| 1106 } |
| 1107 |
| 1108 |
| 1109 void InstructionSelector::VisitInt32SubWithOverflow(Node* node) { |
| 1110 if (Node* ovf = node->FindProjection(1)) { |
| 1111 FlagsContinuation cont(kOverflow, ovf); |
| 1112 return VisitBinop(this, node, kArmSub, kArmRsb, &cont); |
| 1113 } |
| 1114 FlagsContinuation cont; |
| 1115 VisitBinop(this, node, kArmSub, kArmRsb, &cont); |
| 1116 } |
| 1117 |
| 1118 |
| 1119 void InstructionSelector::VisitFloat64Equal(Node* node) { |
| 1120 FlagsContinuation cont(kUnorderedEqual, node); |
| 1121 VisitFloat64Compare(this, node, &cont); |
| 1122 } |
| 1123 |
| 1124 |
| 1125 void InstructionSelector::VisitFloat64LessThan(Node* node) { |
| 1126 FlagsContinuation cont(kUnorderedLessThan, node); |
| 1127 VisitFloat64Compare(this, node, &cont); |
| 1128 } |
| 1129 |
| 1130 |
| 1131 void InstructionSelector::VisitFloat64LessThanOrEqual(Node* node) { |
| 1132 FlagsContinuation cont(kUnorderedLessThanOrEqual, node); |
| 1133 VisitFloat64Compare(this, node, &cont); |
963 } | 1134 } |
964 | 1135 |
965 } // namespace compiler | 1136 } // namespace compiler |
966 } // namespace internal | 1137 } // namespace internal |
967 } // namespace v8 | 1138 } // namespace v8 |
OLD | NEW |