| 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/instruction-selector-impl.h" | 5 #include "src/compiler/instruction-selector-impl.h" |
| 6 #include "src/compiler/node-matchers.h" | 6 #include "src/compiler/node-matchers.h" |
| 7 #include "src/compiler/node-properties.h" | 7 #include "src/compiler/node-properties.h" |
| 8 | 8 |
| 9 namespace v8 { | 9 namespace v8 { |
| 10 namespace internal { | 10 namespace internal { |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 125 void VisitRRO(InstructionSelector* selector, ArchOpcode opcode, Node* node, | 125 void VisitRRO(InstructionSelector* selector, ArchOpcode opcode, Node* node, |
| 126 ImmediateMode operand_mode) { | 126 ImmediateMode operand_mode) { |
| 127 Arm64OperandGenerator g(selector); | 127 Arm64OperandGenerator g(selector); |
| 128 selector->Emit(opcode, g.DefineAsRegister(node), | 128 selector->Emit(opcode, g.DefineAsRegister(node), |
| 129 g.UseRegister(node->InputAt(0)), | 129 g.UseRegister(node->InputAt(0)), |
| 130 g.UseOperand(node->InputAt(1), operand_mode)); | 130 g.UseOperand(node->InputAt(1), operand_mode)); |
| 131 } | 131 } |
| 132 | 132 |
| 133 | 133 |
| 134 bool TryMatchAnyShift(InstructionSelector* selector, Node* node, | 134 bool TryMatchAnyShift(InstructionSelector* selector, Node* node, |
| 135 InstructionCode* opcode, bool try_ror) { | 135 Node* input_node, InstructionCode* opcode, bool try_ror) { |
| 136 Arm64OperandGenerator g(selector); | 136 Arm64OperandGenerator g(selector); |
| 137 | 137 |
| 138 if (node->InputCount() != 2) return false; | 138 if (!selector->CanCover(node, input_node)) return false; |
| 139 if (!g.IsIntegerConstant(node->InputAt(1))) return false; | 139 if (input_node->InputCount() != 2) return false; |
| 140 if (!g.IsIntegerConstant(input_node->InputAt(1))) return false; |
| 140 | 141 |
| 141 switch (node->opcode()) { | 142 switch (input_node->opcode()) { |
| 142 case IrOpcode::kWord32Shl: | 143 case IrOpcode::kWord32Shl: |
| 143 case IrOpcode::kWord64Shl: | 144 case IrOpcode::kWord64Shl: |
| 144 *opcode |= AddressingModeField::encode(kMode_Operand2_R_LSL_I); | 145 *opcode |= AddressingModeField::encode(kMode_Operand2_R_LSL_I); |
| 145 return true; | 146 return true; |
| 146 case IrOpcode::kWord32Shr: | 147 case IrOpcode::kWord32Shr: |
| 147 case IrOpcode::kWord64Shr: | 148 case IrOpcode::kWord64Shr: |
| 148 *opcode |= AddressingModeField::encode(kMode_Operand2_R_LSR_I); | 149 *opcode |= AddressingModeField::encode(kMode_Operand2_R_LSR_I); |
| 149 return true; | 150 return true; |
| 150 case IrOpcode::kWord32Sar: | 151 case IrOpcode::kWord32Sar: |
| 151 case IrOpcode::kWord64Sar: | 152 case IrOpcode::kWord64Sar: |
| 152 *opcode |= AddressingModeField::encode(kMode_Operand2_R_ASR_I); | 153 *opcode |= AddressingModeField::encode(kMode_Operand2_R_ASR_I); |
| 153 return true; | 154 return true; |
| 154 case IrOpcode::kWord32Ror: | 155 case IrOpcode::kWord32Ror: |
| 155 case IrOpcode::kWord64Ror: | 156 case IrOpcode::kWord64Ror: |
| 156 if (try_ror) { | 157 if (try_ror) { |
| 157 *opcode |= AddressingModeField::encode(kMode_Operand2_R_ROR_I); | 158 *opcode |= AddressingModeField::encode(kMode_Operand2_R_ROR_I); |
| 158 return true; | 159 return true; |
| 159 } | 160 } |
| 160 return false; | 161 return false; |
| 161 default: | 162 default: |
| 162 return false; | 163 return false; |
| 163 } | 164 } |
| 164 } | 165 } |
| 165 | 166 |
| 166 | 167 |
| 167 bool TryMatchAnyExtend(Arm64OperandGenerator* g, InstructionSelector* selector, | 168 bool TryMatchAnyExtend(Arm64OperandGenerator* g, InstructionSelector* selector, |
| 168 Node* left_node, Node* right_node, | 169 Node* node, Node* left_node, Node* right_node, |
| 169 InstructionOperand* left_op, | 170 InstructionOperand* left_op, |
| 170 InstructionOperand* right_op, InstructionCode* opcode) { | 171 InstructionOperand* right_op, InstructionCode* opcode) { |
| 172 if (!selector->CanCover(node, right_node)) return false; |
| 173 |
| 171 NodeMatcher nm(right_node); | 174 NodeMatcher nm(right_node); |
| 172 | 175 |
| 173 if (nm.IsWord32And()) { | 176 if (nm.IsWord32And()) { |
| 174 Int32BinopMatcher mright(right_node); | 177 Int32BinopMatcher mright(right_node); |
| 175 if (mright.right().Is(0xff) || mright.right().Is(0xffff)) { | 178 if (mright.right().Is(0xff) || mright.right().Is(0xffff)) { |
| 176 int32_t mask = mright.right().Value(); | 179 int32_t mask = mright.right().Value(); |
| 177 *left_op = g->UseRegister(left_node); | 180 *left_op = g->UseRegister(left_node); |
| 178 *right_op = g->UseRegister(mright.left().node()); | 181 *right_op = g->UseRegister(mright.left().node()); |
| 179 *opcode |= AddressingModeField::encode( | 182 *opcode |= AddressingModeField::encode( |
| 180 (mask == 0xff) ? kMode_Operand2_R_UXTB : kMode_Operand2_R_UXTH); | 183 (mask == 0xff) ? kMode_Operand2_R_UXTB : kMode_Operand2_R_UXTH); |
| (...skipping 19 matching lines...) Expand all Loading... |
| 200 } | 203 } |
| 201 | 204 |
| 202 | 205 |
| 203 // Shared routine for multiple binary operations. | 206 // Shared routine for multiple binary operations. |
| 204 template <typename Matcher> | 207 template <typename Matcher> |
| 205 void VisitBinop(InstructionSelector* selector, Node* node, | 208 void VisitBinop(InstructionSelector* selector, Node* node, |
| 206 InstructionCode opcode, ImmediateMode operand_mode, | 209 InstructionCode opcode, ImmediateMode operand_mode, |
| 207 FlagsContinuation* cont) { | 210 FlagsContinuation* cont) { |
| 208 Arm64OperandGenerator g(selector); | 211 Arm64OperandGenerator g(selector); |
| 209 Matcher m(node); | 212 Matcher m(node); |
| 210 InstructionOperand inputs[4]; | 213 InstructionOperand inputs[5]; |
| 211 size_t input_count = 0; | 214 size_t input_count = 0; |
| 212 InstructionOperand outputs[2]; | 215 InstructionOperand outputs[2]; |
| 213 size_t output_count = 0; | 216 size_t output_count = 0; |
| 214 bool is_add_sub = false; | 217 bool is_cmp = opcode == kArm64Cmp32; |
| 215 | 218 |
| 216 if (m.IsInt32Add() || m.IsInt64Add() || m.IsInt32Sub() || m.IsInt64Sub()) { | 219 // We can commute cmp by switching the inputs and commuting the flags |
| 217 is_add_sub = true; | 220 // continuation. |
| 218 } | 221 bool can_commute = m.HasProperty(Operator::kCommutative) || is_cmp; |
| 222 |
| 223 // The cmp instruction is encoded as sub with zero output register, and |
| 224 // therefore supports the same operand modes. |
| 225 bool is_add_sub = m.IsInt32Add() || m.IsInt64Add() || m.IsInt32Sub() || |
| 226 m.IsInt64Sub() || is_cmp; |
| 219 | 227 |
| 220 Node* left_node = m.left().node(); | 228 Node* left_node = m.left().node(); |
| 221 Node* right_node = m.right().node(); | 229 Node* right_node = m.right().node(); |
| 222 | 230 |
| 223 if (g.CanBeImmediate(right_node, operand_mode)) { | 231 if (g.CanBeImmediate(right_node, operand_mode)) { |
| 224 inputs[input_count++] = g.UseRegister(left_node); | 232 inputs[input_count++] = g.UseRegister(left_node); |
| 225 inputs[input_count++] = g.UseImmediate(right_node); | 233 inputs[input_count++] = g.UseImmediate(right_node); |
| 234 } else if (is_cmp && g.CanBeImmediate(left_node, operand_mode)) { |
| 235 cont->Commute(); |
| 236 inputs[input_count++] = g.UseRegister(right_node); |
| 237 inputs[input_count++] = g.UseImmediate(left_node); |
| 226 } else if (is_add_sub && | 238 } else if (is_add_sub && |
| 227 TryMatchAnyExtend(&g, selector, left_node, right_node, &inputs[0], | 239 TryMatchAnyExtend(&g, selector, node, left_node, right_node, |
| 228 &inputs[1], &opcode)) { | 240 &inputs[0], &inputs[1], &opcode)) { |
| 229 input_count += 2; | 241 input_count += 2; |
| 230 } else if (is_add_sub && m.HasProperty(Operator::kCommutative) && | 242 } else if (is_add_sub && can_commute && |
| 231 TryMatchAnyExtend(&g, selector, right_node, left_node, &inputs[0], | 243 TryMatchAnyExtend(&g, selector, node, right_node, left_node, |
| 232 &inputs[1], &opcode)) { | 244 &inputs[0], &inputs[1], &opcode)) { |
| 245 if (is_cmp) cont->Commute(); |
| 233 input_count += 2; | 246 input_count += 2; |
| 234 } else if (TryMatchAnyShift(selector, right_node, &opcode, !is_add_sub)) { | 247 } else if (TryMatchAnyShift(selector, node, right_node, &opcode, |
| 248 !is_add_sub)) { |
| 235 Matcher m_shift(right_node); | 249 Matcher m_shift(right_node); |
| 236 inputs[input_count++] = g.UseRegister(left_node); | 250 inputs[input_count++] = g.UseRegister(left_node); |
| 237 inputs[input_count++] = g.UseRegister(m_shift.left().node()); | 251 inputs[input_count++] = g.UseRegister(m_shift.left().node()); |
| 238 inputs[input_count++] = g.UseImmediate(m_shift.right().node()); | 252 inputs[input_count++] = g.UseImmediate(m_shift.right().node()); |
| 239 } else if (m.HasProperty(Operator::kCommutative) && | 253 } else if (can_commute && TryMatchAnyShift(selector, node, left_node, &opcode, |
| 240 TryMatchAnyShift(selector, left_node, &opcode, !is_add_sub)) { | 254 !is_add_sub)) { |
| 255 if (is_cmp) cont->Commute(); |
| 241 Matcher m_shift(left_node); | 256 Matcher m_shift(left_node); |
| 242 inputs[input_count++] = g.UseRegister(right_node); | 257 inputs[input_count++] = g.UseRegister(right_node); |
| 243 inputs[input_count++] = g.UseRegister(m_shift.left().node()); | 258 inputs[input_count++] = g.UseRegister(m_shift.left().node()); |
| 244 inputs[input_count++] = g.UseImmediate(m_shift.right().node()); | 259 inputs[input_count++] = g.UseImmediate(m_shift.right().node()); |
| 245 } else { | 260 } else { |
| 246 inputs[input_count++] = g.UseRegister(left_node); | 261 inputs[input_count++] = g.UseRegister(left_node); |
| 247 inputs[input_count++] = g.UseRegister(right_node); | 262 inputs[input_count++] = g.UseRegister(right_node); |
| 248 } | 263 } |
| 249 | 264 |
| 250 if (cont->IsBranch()) { | 265 if (cont->IsBranch()) { |
| 251 inputs[input_count++] = g.Label(cont->true_block()); | 266 inputs[input_count++] = g.Label(cont->true_block()); |
| 252 inputs[input_count++] = g.Label(cont->false_block()); | 267 inputs[input_count++] = g.Label(cont->false_block()); |
| 253 } | 268 } |
| 254 | 269 |
| 255 outputs[output_count++] = g.DefineAsRegister(node); | 270 if (!is_cmp) { |
| 271 outputs[output_count++] = g.DefineAsRegister(node); |
| 272 } |
| 273 |
| 256 if (cont->IsSet()) { | 274 if (cont->IsSet()) { |
| 257 outputs[output_count++] = g.DefineAsRegister(cont->result()); | 275 outputs[output_count++] = g.DefineAsRegister(cont->result()); |
| 258 } | 276 } |
| 259 | 277 |
| 260 DCHECK_NE(0u, input_count); | 278 DCHECK_NE(0u, input_count); |
| 261 DCHECK_NE(0u, output_count); | 279 DCHECK((output_count != 0) || is_cmp); |
| 262 DCHECK_GE(arraysize(inputs), input_count); | 280 DCHECK_GE(arraysize(inputs), input_count); |
| 263 DCHECK_GE(arraysize(outputs), output_count); | 281 DCHECK_GE(arraysize(outputs), output_count); |
| 264 | 282 |
| 265 selector->Emit(cont->Encode(opcode), output_count, outputs, input_count, | 283 selector->Emit(cont->Encode(opcode), output_count, outputs, input_count, |
| 266 inputs); | 284 inputs); |
| 267 } | 285 } |
| 268 | 286 |
| 269 | 287 |
| 270 // Shared routine for multiple binary operations. | 288 // Shared routine for multiple binary operations. |
| 271 template <typename Matcher> | 289 template <typename Matcher> |
| (...skipping 1318 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1590 cont); | 1608 cont); |
| 1591 } else { | 1609 } else { |
| 1592 VisitCompare(selector, opcode, g.UseRegister(left), g.UseRegister(right), | 1610 VisitCompare(selector, opcode, g.UseRegister(left), g.UseRegister(right), |
| 1593 cont); | 1611 cont); |
| 1594 } | 1612 } |
| 1595 } | 1613 } |
| 1596 | 1614 |
| 1597 | 1615 |
| 1598 void VisitWord32Compare(InstructionSelector* selector, Node* node, | 1616 void VisitWord32Compare(InstructionSelector* selector, Node* node, |
| 1599 FlagsContinuation* cont) { | 1617 FlagsContinuation* cont) { |
| 1600 VisitWordCompare(selector, node, kArm64Cmp32, cont, false, kArithmeticImm); | 1618 VisitBinop<Int32BinopMatcher>(selector, node, kArm64Cmp32, kArithmeticImm, |
| 1619 cont); |
| 1601 } | 1620 } |
| 1602 | 1621 |
| 1603 | 1622 |
| 1604 void VisitWordTest(InstructionSelector* selector, Node* node, | 1623 void VisitWordTest(InstructionSelector* selector, Node* node, |
| 1605 InstructionCode opcode, FlagsContinuation* cont) { | 1624 InstructionCode opcode, FlagsContinuation* cont) { |
| 1606 Arm64OperandGenerator g(selector); | 1625 Arm64OperandGenerator g(selector); |
| 1607 VisitCompare(selector, opcode, g.UseRegister(node), g.UseRegister(node), | 1626 VisitCompare(selector, opcode, g.UseRegister(node), g.UseRegister(node), |
| 1608 cont); | 1627 cont); |
| 1609 } | 1628 } |
| 1610 | 1629 |
| (...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1749 default: | 1768 default: |
| 1750 break; | 1769 break; |
| 1751 } | 1770 } |
| 1752 } | 1771 } |
| 1753 } | 1772 } |
| 1754 break; | 1773 break; |
| 1755 case IrOpcode::kInt32Add: | 1774 case IrOpcode::kInt32Add: |
| 1756 return VisitWordCompare(this, value, kArm64Cmn32, &cont, true, | 1775 return VisitWordCompare(this, value, kArm64Cmn32, &cont, true, |
| 1757 kArithmeticImm); | 1776 kArithmeticImm); |
| 1758 case IrOpcode::kInt32Sub: | 1777 case IrOpcode::kInt32Sub: |
| 1759 return VisitWordCompare(this, value, kArm64Cmp32, &cont, false, | 1778 return VisitWord32Compare(this, value, &cont); |
| 1760 kArithmeticImm); | |
| 1761 case IrOpcode::kWord32And: { | 1779 case IrOpcode::kWord32And: { |
| 1762 Int32BinopMatcher m(value); | 1780 Int32BinopMatcher m(value); |
| 1763 if (m.right().HasValue() && | 1781 if (m.right().HasValue() && |
| 1764 (base::bits::CountPopulation32(m.right().Value()) == 1)) { | 1782 (base::bits::CountPopulation32(m.right().Value()) == 1)) { |
| 1765 // If the mask has only one bit set, we can use tbz/tbnz. | 1783 // If the mask has only one bit set, we can use tbz/tbnz. |
| 1766 DCHECK((cont.condition() == kEqual) || | 1784 DCHECK((cont.condition() == kEqual) || |
| 1767 (cont.condition() == kNotEqual)); | 1785 (cont.condition() == kNotEqual)); |
| 1768 Emit(cont.Encode(kArm64TestAndBranch32), g.NoOutput(), | 1786 Emit(cont.Encode(kArm64TestAndBranch32), g.NoOutput(), |
| 1769 g.UseRegister(m.left().node()), | 1787 g.UseRegister(m.left().node()), |
| 1770 g.TempImmediate( | 1788 g.TempImmediate( |
| (...skipping 265 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2036 MachineOperatorBuilder::kFloat64RoundTruncate | | 2054 MachineOperatorBuilder::kFloat64RoundTruncate | |
| 2037 MachineOperatorBuilder::kFloat64RoundTiesAway | | 2055 MachineOperatorBuilder::kFloat64RoundTiesAway | |
| 2038 MachineOperatorBuilder::kWord32ShiftIsSafe | | 2056 MachineOperatorBuilder::kWord32ShiftIsSafe | |
| 2039 MachineOperatorBuilder::kInt32DivIsSafe | | 2057 MachineOperatorBuilder::kInt32DivIsSafe | |
| 2040 MachineOperatorBuilder::kUint32DivIsSafe; | 2058 MachineOperatorBuilder::kUint32DivIsSafe; |
| 2041 } | 2059 } |
| 2042 | 2060 |
| 2043 } // namespace compiler | 2061 } // namespace compiler |
| 2044 } // namespace internal | 2062 } // namespace internal |
| 2045 } // namespace v8 | 2063 } // namespace v8 |
| OLD | NEW |