| Index: src/compiler/arm64/instruction-selector-arm64.cc
|
| diff --git a/src/compiler/arm64/instruction-selector-arm64.cc b/src/compiler/arm64/instruction-selector-arm64.cc
|
| index abe69f46186370fe1eb792d7c7347af30b7b5ce0..1c2f486a5256cfec6f1fe45ddacdb56e2bc30e97 100644
|
| --- a/src/compiler/arm64/instruction-selector-arm64.cc
|
| +++ b/src/compiler/arm64/instruction-selector-arm64.cc
|
| @@ -37,15 +37,31 @@ class Arm64OperandGenerator final : public OperandGenerator {
|
| return UseRegister(node);
|
| }
|
|
|
| + // Use the provided node if it has the required value, or create a
|
| + // TempImmediate otherwise.
|
| + InstructionOperand UseImmediateOrTemp(Node* node, int32_t value) {
|
| + if (GetIntegerConstantValue(node) == value) {
|
| + return UseImmediate(node);
|
| + }
|
| + return TempImmediate(value);
|
| + }
|
| +
|
| + bool IsIntegerConstant(Node* node) {
|
| + return (node->opcode() == IrOpcode::kInt32Constant) ||
|
| + (node->opcode() == IrOpcode::kInt64Constant);
|
| + }
|
| +
|
| + int64_t GetIntegerConstantValue(Node* node) {
|
| + if (node->opcode() == IrOpcode::kInt32Constant) {
|
| + return OpParameter<int32_t>(node);
|
| + }
|
| + DCHECK(node->opcode() == IrOpcode::kInt64Constant);
|
| + return OpParameter<int64_t>(node);
|
| + }
|
| +
|
| bool CanBeImmediate(Node* node, ImmediateMode mode) {
|
| - int64_t value;
|
| - if (node->opcode() == IrOpcode::kInt32Constant)
|
| - value = OpParameter<int32_t>(node);
|
| - else if (node->opcode() == IrOpcode::kInt64Constant)
|
| - value = OpParameter<int64_t>(node);
|
| - else
|
| - return false;
|
| - return CanBeImmediate(value, mode);
|
| + return IsIntegerConstant(node) &&
|
| + CanBeImmediate(GetIntegerConstantValue(node), mode);
|
| }
|
|
|
| bool CanBeImmediate(int64_t value, ImmediateMode mode) {
|
| @@ -61,10 +77,6 @@ class Arm64OperandGenerator final : public OperandGenerator {
|
| &ignored, &ignored, &ignored);
|
| case kArithmeticImm:
|
| return Assembler::IsImmAddSub(value);
|
| - case kShift32Imm:
|
| - return 0 <= value && value < 32;
|
| - case kShift64Imm:
|
| - return 0 <= value && value < 64;
|
| case kLoadStoreImm8:
|
| return IsLoadStoreImmediate(value, LSByte);
|
| case kLoadStoreImm16:
|
| @@ -75,6 +87,12 @@ class Arm64OperandGenerator final : public OperandGenerator {
|
| return IsLoadStoreImmediate(value, LSDoubleWord);
|
| case kNoImmediate:
|
| return false;
|
| + case kShift32Imm: // Fall through.
|
| + case kShift64Imm:
|
| + // Shift operations only observe the bottom 5 or 6 bits of the value.
|
| + // All possible shifts can be encoded by discarding bits which have no
|
| + // effect.
|
| + return true;
|
| }
|
| return false;
|
| }
|
| @@ -113,47 +131,36 @@ void VisitRRO(InstructionSelector* selector, ArchOpcode opcode, Node* node,
|
| }
|
|
|
|
|
| -template <typename Matcher>
|
| -bool TryMatchShift(InstructionSelector* selector, Node* node,
|
| - InstructionCode* opcode, IrOpcode::Value shift_opcode,
|
| - ImmediateMode imm_mode, AddressingMode addressing_mode) {
|
| - if (node->opcode() != shift_opcode) return false;
|
| - Arm64OperandGenerator g(selector);
|
| - Matcher m(node);
|
| - if (g.CanBeImmediate(m.right().node(), imm_mode)) {
|
| - *opcode |= AddressingModeField::encode(addressing_mode);
|
| - return true;
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -
|
| bool TryMatchAnyShift(InstructionSelector* selector, Node* node,
|
| InstructionCode* opcode, bool try_ror) {
|
| - return TryMatchShift<Int32BinopMatcher>(selector, node, opcode,
|
| - IrOpcode::kWord32Shl, kShift32Imm,
|
| - kMode_Operand2_R_LSL_I) ||
|
| - TryMatchShift<Int32BinopMatcher>(selector, node, opcode,
|
| - IrOpcode::kWord32Shr, kShift32Imm,
|
| - kMode_Operand2_R_LSR_I) ||
|
| - TryMatchShift<Int32BinopMatcher>(selector, node, opcode,
|
| - IrOpcode::kWord32Sar, kShift32Imm,
|
| - kMode_Operand2_R_ASR_I) ||
|
| - (try_ror && TryMatchShift<Int32BinopMatcher>(
|
| - selector, node, opcode, IrOpcode::kWord32Ror,
|
| - kShift32Imm, kMode_Operand2_R_ROR_I)) ||
|
| - TryMatchShift<Int64BinopMatcher>(selector, node, opcode,
|
| - IrOpcode::kWord64Shl, kShift64Imm,
|
| - kMode_Operand2_R_LSL_I) ||
|
| - TryMatchShift<Int64BinopMatcher>(selector, node, opcode,
|
| - IrOpcode::kWord64Shr, kShift64Imm,
|
| - kMode_Operand2_R_LSR_I) ||
|
| - TryMatchShift<Int64BinopMatcher>(selector, node, opcode,
|
| - IrOpcode::kWord64Sar, kShift64Imm,
|
| - kMode_Operand2_R_ASR_I) ||
|
| - (try_ror && TryMatchShift<Int64BinopMatcher>(
|
| - selector, node, opcode, IrOpcode::kWord64Ror,
|
| - kShift64Imm, kMode_Operand2_R_ROR_I));
|
| + Arm64OperandGenerator g(selector);
|
| +
|
| + if (node->InputCount() != 2) return false;
|
| + if (!g.IsIntegerConstant(node->InputAt(1))) return false;
|
| +
|
| + switch (node->opcode()) {
|
| + case IrOpcode::kWord32Shl:
|
| + case IrOpcode::kWord64Shl:
|
| + *opcode |= AddressingModeField::encode(kMode_Operand2_R_LSL_I);
|
| + return true;
|
| + case IrOpcode::kWord32Shr:
|
| + case IrOpcode::kWord64Shr:
|
| + *opcode |= AddressingModeField::encode(kMode_Operand2_R_LSR_I);
|
| + return true;
|
| + case IrOpcode::kWord32Sar:
|
| + case IrOpcode::kWord64Sar:
|
| + *opcode |= AddressingModeField::encode(kMode_Operand2_R_ASR_I);
|
| + return true;
|
| + case IrOpcode::kWord32Ror:
|
| + case IrOpcode::kWord64Ror:
|
| + if (try_ror) {
|
| + *opcode |= AddressingModeField::encode(kMode_Operand2_R_ROR_I);
|
| + return true;
|
| + }
|
| + return false;
|
| + default:
|
| + return false;
|
| + }
|
| }
|
|
|
|
|
| @@ -548,17 +555,20 @@ void InstructionSelector::VisitWord32And(Node* node) {
|
| // Select Ubfx for And(Shr(x, imm), mask) where the mask is in the least
|
| // significant bits.
|
| Int32BinopMatcher mleft(m.left().node());
|
| - if (mleft.right().IsInRange(0, 31)) {
|
| + if (mleft.right().HasValue()) {
|
| + // Any shift value can match; int32 shifts use `value % 32`.
|
| + uint32_t lsb = mleft.right().Value() & 0x1f;
|
| +
|
| // Ubfx cannot extract bits past the register size, however since
|
| // shifting the original value would have introduced some zeros we can
|
| // still use ubfx with a smaller mask and the remaining bits will be
|
| // zeros.
|
| - uint32_t lsb = mleft.right().Value();
|
| if (lsb + mask_width > 32) mask_width = 32 - lsb;
|
|
|
| Emit(kArm64Ubfx32, g.DefineAsRegister(node),
|
| g.UseRegister(mleft.left().node()),
|
| - g.UseImmediate(mleft.right().node()), g.TempImmediate(mask_width));
|
| + g.UseImmediateOrTemp(mleft.right().node(), lsb),
|
| + g.TempImmediate(mask_width));
|
| return;
|
| }
|
| // Other cases fall through to the normal And operation.
|
| @@ -585,17 +595,19 @@ void InstructionSelector::VisitWord64And(Node* node) {
|
| // Select Ubfx for And(Shr(x, imm), mask) where the mask is in the least
|
| // significant bits.
|
| Int64BinopMatcher mleft(m.left().node());
|
| - if (mleft.right().IsInRange(0, 63)) {
|
| + if (mleft.right().HasValue()) {
|
| + // Any shift value can match; int64 shifts use `value % 64`.
|
| + uint32_t lsb = static_cast<uint32_t>(mleft.right().Value() & 0x3f);
|
| +
|
| // Ubfx cannot extract bits past the register size, however since
|
| // shifting the original value would have introduced some zeros we can
|
| // still use ubfx with a smaller mask and the remaining bits will be
|
| // zeros.
|
| - uint64_t lsb = mleft.right().Value();
|
| if (lsb + mask_width > 64) mask_width = 64 - lsb;
|
|
|
| Emit(kArm64Ubfx, g.DefineAsRegister(node),
|
| g.UseRegister(mleft.left().node()),
|
| - g.UseImmediate(mleft.right().node()),
|
| + g.UseImmediateOrTemp(mleft.right().node(), lsb),
|
| g.TempImmediate(static_cast<int32_t>(mask_width)));
|
| return;
|
| }
|
| @@ -724,20 +736,21 @@ bool TryEmitBitfieldExtract32(InstructionSelector* selector, Node* node) {
|
|
|
| void InstructionSelector::VisitWord32Shr(Node* node) {
|
| Int32BinopMatcher m(node);
|
| - if (m.left().IsWord32And() && m.right().IsInRange(0, 31)) {
|
| - uint32_t lsb = m.right().Value();
|
| + if (m.left().IsWord32And() && m.right().HasValue()) {
|
| + uint32_t lsb = m.right().Value() & 0x1f;
|
| Int32BinopMatcher mleft(m.left().node());
|
| if (mleft.right().HasValue()) {
|
| + // Select Ubfx for Shr(And(x, mask), imm) where the result of the mask is
|
| + // shifted into the least-significant bits.
|
| uint32_t mask = (mleft.right().Value() >> lsb) << lsb;
|
| - uint32_t mask_width = base::bits::CountPopulation32(mask);
|
| - uint32_t mask_msb = base::bits::CountLeadingZeros32(mask);
|
| - // Select Ubfx for Shr(And(x, mask), imm) where the result of the mask is
|
| - // shifted into the least-significant bits.
|
| + unsigned mask_width = base::bits::CountPopulation32(mask);
|
| + unsigned mask_msb = base::bits::CountLeadingZeros32(mask);
|
| if ((mask_msb + mask_width + lsb) == 32) {
|
| Arm64OperandGenerator g(this);
|
| DCHECK_EQ(lsb, base::bits::CountTrailingZeros32(mask));
|
| Emit(kArm64Ubfx32, g.DefineAsRegister(node),
|
| - g.UseRegister(mleft.left().node()), g.TempImmediate(lsb),
|
| + g.UseRegister(mleft.left().node()),
|
| + g.UseImmediateOrTemp(m.right().node(), lsb),
|
| g.TempImmediate(mask_width));
|
| return;
|
| }
|
| @@ -766,23 +779,23 @@ void InstructionSelector::VisitWord32Shr(Node* node) {
|
|
|
|
|
| void InstructionSelector::VisitWord64Shr(Node* node) {
|
| - Arm64OperandGenerator g(this);
|
| Int64BinopMatcher m(node);
|
| - if (m.left().IsWord64And() && m.right().IsInRange(0, 63)) {
|
| - uint64_t lsb = m.right().Value();
|
| + if (m.left().IsWord64And() && m.right().HasValue()) {
|
| + uint32_t lsb = m.right().Value() & 0x3f;
|
| Int64BinopMatcher mleft(m.left().node());
|
| if (mleft.right().HasValue()) {
|
| // Select Ubfx for Shr(And(x, mask), imm) where the result of the mask is
|
| // shifted into the least-significant bits.
|
| uint64_t mask = (mleft.right().Value() >> lsb) << lsb;
|
| - uint64_t mask_width = base::bits::CountPopulation64(mask);
|
| - uint64_t mask_msb = base::bits::CountLeadingZeros64(mask);
|
| + unsigned mask_width = base::bits::CountPopulation64(mask);
|
| + unsigned mask_msb = base::bits::CountLeadingZeros64(mask);
|
| if ((mask_msb + mask_width + lsb) == 64) {
|
| + Arm64OperandGenerator g(this);
|
| DCHECK_EQ(lsb, base::bits::CountTrailingZeros64(mask));
|
| Emit(kArm64Ubfx, g.DefineAsRegister(node),
|
| g.UseRegister(mleft.left().node()),
|
| - g.TempImmediate(static_cast<int32_t>(lsb)),
|
| - g.TempImmediate(static_cast<int32_t>(mask_width)));
|
| + g.UseImmediateOrTemp(m.right().node(), lsb),
|
| + g.TempImmediate(mask_width));
|
| return;
|
| }
|
| }
|
|
|