diff options
author | 2017-01-13 14:42:47 +0000 | |
---|---|---|
committer | 2017-02-17 14:59:27 +0000 | |
commit | 74234daabb28a4b9c804bf8bf908e7334bd4d400 (patch) | |
tree | 0b60cb00ab117c1a9a4b92983514962198b548bf /compiler | |
parent | a7e9bfafeb64b1142433a41b05ddc263cadc61e3 (diff) |
ARM: Merge data-processing instructions and shifts/(un)signed extensions
This commit mirrors the work that has already been done for ARM64.
Test: m test-art-target-run-test-551-checker-shifter-operand
Change-Id: Iec8c1563b035f40f0e18dcffde28d91dc21922f8
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/Android.bp | 2 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm.cc | 253 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm64.cc | 21 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm_vixl.cc | 244 | ||||
-rw-r--r-- | compiler/optimizing/common_arm.h | 12 | ||||
-rw-r--r-- | compiler/optimizing/common_arm64.h | 52 | ||||
-rw-r--r-- | compiler/optimizing/graph_visualizer.cc | 6 | ||||
-rw-r--r-- | compiler/optimizing/instruction_simplifier_arm.cc | 155 | ||||
-rw-r--r-- | compiler/optimizing/instruction_simplifier_arm.h | 34 | ||||
-rw-r--r-- | compiler/optimizing/instruction_simplifier_arm64.cc | 29 | ||||
-rw-r--r-- | compiler/optimizing/instruction_simplifier_arm64.h | 4 | ||||
-rw-r--r-- | compiler/optimizing/instruction_simplifier_shared.h | 27 | ||||
-rw-r--r-- | compiler/optimizing/nodes.h | 9 | ||||
-rw-r--r-- | compiler/optimizing/nodes_arm64.h | 99 | ||||
-rw-r--r-- | compiler/optimizing/nodes_shared.cc (renamed from compiler/optimizing/nodes_arm64.cc) | 41 | ||||
-rw-r--r-- | compiler/optimizing/nodes_shared.h | 75 | ||||
-rw-r--r-- | compiler/optimizing/scheduler_arm64.cc | 4 | ||||
-rw-r--r-- | compiler/optimizing/scheduler_arm64.h | 3 |
18 files changed, 861 insertions, 209 deletions
diff --git a/compiler/Android.bp b/compiler/Android.bp index f6a4db49fb..f5589cd7a3 100644 --- a/compiler/Android.bp +++ b/compiler/Android.bp @@ -111,6 +111,7 @@ art_cc_defaults { "optimizing/instruction_simplifier_shared.cc", "optimizing/intrinsics_arm.cc", "optimizing/intrinsics_arm_vixl.cc", + "optimizing/nodes_shared.cc", "utils/arm/assembler_arm.cc", "utils/arm/assembler_arm_vixl.cc", "utils/arm/assembler_thumb2.cc", @@ -127,7 +128,6 @@ art_cc_defaults { "optimizing/scheduler_arm64.cc", "optimizing/instruction_simplifier_arm64.cc", "optimizing/intrinsics_arm64.cc", - "optimizing/nodes_arm64.cc", "utils/arm64/assembler_arm64.cc", "utils/arm64/jni_macro_assembler_arm64.cc", "utils/arm64/managed_register_arm64.cc", diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 759a951d6b..7b84ef83cd 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -19,6 +19,7 @@ #include "arch/arm/instruction_set_features_arm.h" #include "art_method.h" #include "code_generator_utils.h" +#include "common_arm.h" #include "compiled_method.h" #include "entrypoints/quick/quick_entrypoints.h" #include "gc/accounting/card_table.h" @@ -1132,10 +1133,6 @@ class ReadBarrierForRootSlowPathARM : public SlowPathCodeARM { DISALLOW_COPY_AND_ASSIGN(ReadBarrierForRootSlowPathARM); }; -#undef __ -// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy. -#define __ down_cast<ArmAssembler*>(GetAssembler())-> // NOLINT - inline Condition ARMCondition(IfCondition cond) { switch (cond) { case kCondEQ: return EQ; @@ -1191,6 +1188,197 @@ inline Condition ARMFPCondition(IfCondition cond, bool gt_bias) { } } +inline Shift ShiftFromOpKind(HDataProcWithShifterOp::OpKind op_kind) { + switch (op_kind) { + case HDataProcWithShifterOp::kASR: return ASR; + case HDataProcWithShifterOp::kLSL: return LSL; + case HDataProcWithShifterOp::kLSR: return LSR; + default: + LOG(FATAL) << "Unexpected op kind " << op_kind; + UNREACHABLE(); + } +} + +static void GenerateDataProcInstruction(HInstruction::InstructionKind kind, + Register out, + Register first, + const ShifterOperand& second, + CodeGeneratorARM* codegen) { + if (second.IsImmediate() && second.GetImmediate() == 0) { + const ShifterOperand in = kind == HInstruction::kAnd + ? ShifterOperand(0) + : ShifterOperand(first); + + __ mov(out, in); + } else { + switch (kind) { + case HInstruction::kAdd: + __ add(out, first, second); + break; + case HInstruction::kAnd: + __ and_(out, first, second); + break; + case HInstruction::kOr: + __ orr(out, first, second); + break; + case HInstruction::kSub: + __ sub(out, first, second); + break; + case HInstruction::kXor: + __ eor(out, first, second); + break; + default: + LOG(FATAL) << "Unexpected instruction kind: " << kind; + UNREACHABLE(); + } + } +} + +static void GenerateDataProc(HInstruction::InstructionKind kind, + const Location& out, + const Location& first, + const ShifterOperand& second_lo, + const ShifterOperand& second_hi, + CodeGeneratorARM* codegen) { + const Register first_hi = first.AsRegisterPairHigh<Register>(); + const Register first_lo = first.AsRegisterPairLow<Register>(); + const Register out_hi = out.AsRegisterPairHigh<Register>(); + const Register out_lo = out.AsRegisterPairLow<Register>(); + + if (kind == HInstruction::kAdd) { + __ adds(out_lo, first_lo, second_lo); + __ adc(out_hi, first_hi, second_hi); + } else if (kind == HInstruction::kSub) { + __ subs(out_lo, first_lo, second_lo); + __ sbc(out_hi, first_hi, second_hi); + } else { + GenerateDataProcInstruction(kind, out_lo, first_lo, second_lo, codegen); + GenerateDataProcInstruction(kind, out_hi, first_hi, second_hi, codegen); + } +} + +static ShifterOperand GetShifterOperand(Register rm, Shift shift, uint32_t shift_imm) { + return shift_imm == 0 ? ShifterOperand(rm) : ShifterOperand(rm, shift, shift_imm); +} + +static void GenerateLongDataProc(HDataProcWithShifterOp* instruction, CodeGeneratorARM* codegen) { + DCHECK_EQ(instruction->GetType(), Primitive::kPrimLong); + DCHECK(HDataProcWithShifterOp::IsShiftOp(instruction->GetOpKind())); + + const LocationSummary* const locations = instruction->GetLocations(); + const uint32_t shift_value = instruction->GetShiftAmount(); + const HInstruction::InstructionKind kind = instruction->GetInstrKind(); + const Location first = locations->InAt(0); + const Location second = locations->InAt(1); + const Location out = locations->Out(); + const Register first_hi = first.AsRegisterPairHigh<Register>(); + const Register first_lo = first.AsRegisterPairLow<Register>(); + const Register out_hi = out.AsRegisterPairHigh<Register>(); + const Register out_lo = out.AsRegisterPairLow<Register>(); + const Register second_hi = second.AsRegisterPairHigh<Register>(); + const Register second_lo = second.AsRegisterPairLow<Register>(); + const Shift shift = ShiftFromOpKind(instruction->GetOpKind()); + + if (shift_value >= 32) { + if (shift == LSL) { + GenerateDataProcInstruction(kind, + out_hi, + first_hi, + ShifterOperand(second_lo, LSL, shift_value - 32), + codegen); + GenerateDataProcInstruction(kind, + out_lo, + first_lo, + ShifterOperand(0), + codegen); + } else if (shift == ASR) { + GenerateDataProc(kind, + out, + first, + GetShifterOperand(second_hi, ASR, shift_value - 32), + ShifterOperand(second_hi, ASR, 31), + codegen); + } else { + DCHECK_EQ(shift, LSR); + GenerateDataProc(kind, + out, + first, + GetShifterOperand(second_hi, LSR, shift_value - 32), + ShifterOperand(0), + codegen); + } + } else { + DCHECK_GT(shift_value, 1U); + DCHECK_LT(shift_value, 32U); + + if (shift == LSL) { + // We are not doing this for HInstruction::kAdd because the output will require + // Location::kOutputOverlap; not applicable to other cases. + if (kind == HInstruction::kOr || kind == HInstruction::kXor) { + GenerateDataProcInstruction(kind, + out_hi, + first_hi, + ShifterOperand(second_hi, LSL, shift_value), + codegen); + GenerateDataProcInstruction(kind, + out_hi, + out_hi, + ShifterOperand(second_lo, LSR, 32 - shift_value), + codegen); + GenerateDataProcInstruction(kind, + out_lo, + first_lo, + ShifterOperand(second_lo, LSL, shift_value), + codegen); + } else { + __ Lsl(IP, second_hi, shift_value); + __ orr(IP, IP, ShifterOperand(second_lo, LSR, 32 - shift_value)); + GenerateDataProc(kind, + out, + first, + ShifterOperand(second_lo, LSL, shift_value), + ShifterOperand(IP), + codegen); + } + } else { + DCHECK(shift == ASR || shift == LSR); + + // We are not doing this for HInstruction::kAdd because the output will require + // Location::kOutputOverlap; not applicable to other cases. + if (kind == HInstruction::kOr || kind == HInstruction::kXor) { + GenerateDataProcInstruction(kind, + out_lo, + first_lo, + ShifterOperand(second_lo, LSR, shift_value), + codegen); + GenerateDataProcInstruction(kind, + out_lo, + out_lo, + ShifterOperand(second_hi, LSL, 32 - shift_value), + codegen); + GenerateDataProcInstruction(kind, + out_hi, + first_hi, + ShifterOperand(second_hi, shift, shift_value), + codegen); + } else { + __ Lsr(IP, second_lo, shift_value); + __ orr(IP, IP, ShifterOperand(second_hi, LSL, 32 - shift_value)); + GenerateDataProc(kind, + out, + first, + ShifterOperand(IP), + ShifterOperand(second_hi, shift, shift_value), + codegen); + } + } + } +} + +#undef __ +// NOLINT on __ macro to suppress wrong warning/fix (misc-macro-parentheses) from clang-tidy. +#define __ down_cast<ArmAssembler*>(GetAssembler())-> // NOLINT + void CodeGeneratorARM::DumpCoreRegister(std::ostream& stream, int reg) const { stream << Register(reg); } @@ -6709,6 +6897,63 @@ void InstructionCodeGeneratorARM::VisitBitwiseNegatedRight(HBitwiseNegatedRight* } } +void LocationsBuilderARM::VisitDataProcWithShifterOp( + HDataProcWithShifterOp* instruction) { + DCHECK(instruction->GetType() == Primitive::kPrimInt || + instruction->GetType() == Primitive::kPrimLong); + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + const bool overlap = instruction->GetType() == Primitive::kPrimLong && + HDataProcWithShifterOp::IsExtensionOp(instruction->GetOpKind()); + + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), + overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorARM::VisitDataProcWithShifterOp( + HDataProcWithShifterOp* instruction) { + const LocationSummary* const locations = instruction->GetLocations(); + const HInstruction::InstructionKind kind = instruction->GetInstrKind(); + const HDataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind(); + const Location left = locations->InAt(0); + const Location right = locations->InAt(1); + const Location out = locations->Out(); + + if (instruction->GetType() == Primitive::kPrimInt) { + DCHECK(!HDataProcWithShifterOp::IsExtensionOp(op_kind)); + + const Register second = instruction->InputAt(1)->GetType() == Primitive::kPrimLong + ? right.AsRegisterPairLow<Register>() + : right.AsRegister<Register>(); + + GenerateDataProcInstruction(kind, + out.AsRegister<Register>(), + left.AsRegister<Register>(), + ShifterOperand(second, + ShiftFromOpKind(op_kind), + instruction->GetShiftAmount()), + codegen_); + } else { + DCHECK_EQ(instruction->GetType(), Primitive::kPrimLong); + + if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) { + const Register second = right.AsRegister<Register>(); + + DCHECK_NE(out.AsRegisterPairLow<Register>(), second); + GenerateDataProc(kind, + out, + left, + ShifterOperand(second), + ShifterOperand(second, ASR, 31), + codegen_); + } else { + GenerateLongDataProc(instruction, codegen_); + } + } +} + void InstructionCodeGeneratorARM::GenerateAndConst(Register out, Register first, uint32_t value) { // Optimize special cases for individual halfs of `and-long` (`and` is simplified earlier). if (value == 0xffffffffu) { diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index e6032d2381..edccbd4904 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -2277,8 +2277,8 @@ void InstructionCodeGeneratorARM64::VisitBitwiseNegatedRight(HBitwiseNegatedRigh } } -void LocationsBuilderARM64::VisitArm64DataProcWithShifterOp( - HArm64DataProcWithShifterOp* instruction) { +void LocationsBuilderARM64::VisitDataProcWithShifterOp( + HDataProcWithShifterOp* instruction) { DCHECK(instruction->GetType() == Primitive::kPrimInt || instruction->GetType() == Primitive::kPrimLong); LocationSummary* locations = @@ -2292,8 +2292,8 @@ void LocationsBuilderARM64::VisitArm64DataProcWithShifterOp( locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); } -void InstructionCodeGeneratorARM64::VisitArm64DataProcWithShifterOp( - HArm64DataProcWithShifterOp* instruction) { +void InstructionCodeGeneratorARM64::VisitDataProcWithShifterOp( + HDataProcWithShifterOp* instruction) { Primitive::Type type = instruction->GetType(); HInstruction::InstructionKind kind = instruction->GetInstrKind(); DCHECK(type == Primitive::kPrimInt || type == Primitive::kPrimLong); @@ -2302,21 +2302,20 @@ void InstructionCodeGeneratorARM64::VisitArm64DataProcWithShifterOp( if (kind != HInstruction::kNeg) { left = InputRegisterAt(instruction, 0); } - // If this `HArm64DataProcWithShifterOp` was created by merging a type conversion as the + // If this `HDataProcWithShifterOp` was created by merging a type conversion as the // shifter operand operation, the IR generating `right_reg` (input to the type // conversion) can have a different type from the current instruction's type, // so we manually indicate the type. Register right_reg = RegisterFrom(instruction->GetLocations()->InAt(1), type); - int64_t shift_amount = instruction->GetShiftAmount() & - (type == Primitive::kPrimInt ? kMaxIntShiftDistance : kMaxLongShiftDistance); - Operand right_operand(0); - HArm64DataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind(); - if (HArm64DataProcWithShifterOp::IsExtensionOp(op_kind)) { + HDataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind(); + if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) { right_operand = Operand(right_reg, helpers::ExtendFromOpKind(op_kind)); } else { - right_operand = Operand(right_reg, helpers::ShiftFromOpKind(op_kind), shift_amount); + right_operand = Operand(right_reg, + helpers::ShiftFromOpKind(op_kind), + instruction->GetShiftAmount()); } // Logical binary operations do not support extension operations in the diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 5c4ca5bc17..6bfbe4a9c9 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -1216,6 +1216,17 @@ inline vixl32::Condition ARMFPCondition(IfCondition cond, bool gt_bias) { } } +inline ShiftType ShiftFromOpKind(HDataProcWithShifterOp::OpKind op_kind) { + switch (op_kind) { + case HDataProcWithShifterOp::kASR: return ShiftType::ASR; + case HDataProcWithShifterOp::kLSL: return ShiftType::LSL; + case HDataProcWithShifterOp::kLSR: return ShiftType::LSR; + default: + LOG(FATAL) << "Unexpected op kind " << op_kind; + UNREACHABLE(); + } +} + void CodeGeneratorARMVIXL::DumpCoreRegister(std::ostream& stream, int reg) const { stream << vixl32::Register(reg); } @@ -1260,6 +1271,185 @@ size_t CodeGeneratorARMVIXL::RestoreFloatingPointRegister(size_t stack_index ATT return 0; } +static void GenerateDataProcInstruction(HInstruction::InstructionKind kind, + vixl32::Register out, + vixl32::Register first, + const Operand& second, + CodeGeneratorARMVIXL* codegen) { + if (second.IsImmediate() && second.GetImmediate() == 0) { + const Operand in = kind == HInstruction::kAnd + ? Operand(0) + : Operand(first); + + __ Mov(out, in); + } else { + switch (kind) { + case HInstruction::kAdd: + __ Add(out, first, second); + break; + case HInstruction::kAnd: + __ And(out, first, second); + break; + case HInstruction::kOr: + __ Orr(out, first, second); + break; + case HInstruction::kSub: + __ Sub(out, first, second); + break; + case HInstruction::kXor: + __ Eor(out, first, second); + break; + default: + LOG(FATAL) << "Unexpected instruction kind: " << kind; + UNREACHABLE(); + } + } +} + +static void GenerateDataProc(HInstruction::InstructionKind kind, + const Location& out, + const Location& first, + const Operand& second_lo, + const Operand& second_hi, + CodeGeneratorARMVIXL* codegen) { + const vixl32::Register first_hi = HighRegisterFrom(first); + const vixl32::Register first_lo = LowRegisterFrom(first); + const vixl32::Register out_hi = HighRegisterFrom(out); + const vixl32::Register out_lo = LowRegisterFrom(out); + + if (kind == HInstruction::kAdd) { + __ Adds(out_lo, first_lo, second_lo); + __ Adc(out_hi, first_hi, second_hi); + } else if (kind == HInstruction::kSub) { + __ Subs(out_lo, first_lo, second_lo); + __ Sbc(out_hi, first_hi, second_hi); + } else { + GenerateDataProcInstruction(kind, out_lo, first_lo, second_lo, codegen); + GenerateDataProcInstruction(kind, out_hi, first_hi, second_hi, codegen); + } +} + +static Operand GetShifterOperand(vixl32::Register rm, ShiftType shift, uint32_t shift_imm) { + return shift_imm == 0 ? Operand(rm) : Operand(rm, shift, shift_imm); +} + +static void GenerateLongDataProc(HDataProcWithShifterOp* instruction, + CodeGeneratorARMVIXL* codegen) { + DCHECK_EQ(instruction->GetType(), Primitive::kPrimLong); + DCHECK(HDataProcWithShifterOp::IsShiftOp(instruction->GetOpKind())); + + const LocationSummary* const locations = instruction->GetLocations(); + const uint32_t shift_value = instruction->GetShiftAmount(); + const HInstruction::InstructionKind kind = instruction->GetInstrKind(); + const Location first = locations->InAt(0); + const Location second = locations->InAt(1); + const Location out = locations->Out(); + const vixl32::Register first_hi = HighRegisterFrom(first); + const vixl32::Register first_lo = LowRegisterFrom(first); + const vixl32::Register out_hi = HighRegisterFrom(out); + const vixl32::Register out_lo = LowRegisterFrom(out); + const vixl32::Register second_hi = HighRegisterFrom(second); + const vixl32::Register second_lo = LowRegisterFrom(second); + const ShiftType shift = ShiftFromOpKind(instruction->GetOpKind()); + + if (shift_value >= 32) { + if (shift == ShiftType::LSL) { + GenerateDataProcInstruction(kind, + out_hi, + first_hi, + Operand(second_lo, ShiftType::LSL, shift_value - 32), + codegen); + GenerateDataProcInstruction(kind, out_lo, first_lo, 0, codegen); + } else if (shift == ShiftType::ASR) { + GenerateDataProc(kind, + out, + first, + GetShifterOperand(second_hi, ShiftType::ASR, shift_value - 32), + Operand(second_hi, ShiftType::ASR, 31), + codegen); + } else { + DCHECK_EQ(shift, ShiftType::LSR); + GenerateDataProc(kind, + out, + first, + GetShifterOperand(second_hi, ShiftType::LSR, shift_value - 32), + 0, + codegen); + } + } else { + DCHECK_GT(shift_value, 1U); + DCHECK_LT(shift_value, 32U); + + UseScratchRegisterScope temps(codegen->GetVIXLAssembler()); + + if (shift == ShiftType::LSL) { + // We are not doing this for HInstruction::kAdd because the output will require + // Location::kOutputOverlap; not applicable to other cases. + if (kind == HInstruction::kOr || kind == HInstruction::kXor) { + GenerateDataProcInstruction(kind, + out_hi, + first_hi, + Operand(second_hi, ShiftType::LSL, shift_value), + codegen); + GenerateDataProcInstruction(kind, + out_hi, + out_hi, + Operand(second_lo, ShiftType::LSR, 32 - shift_value), + codegen); + GenerateDataProcInstruction(kind, + out_lo, + first_lo, + Operand(second_lo, ShiftType::LSL, shift_value), + codegen); + } else { + const vixl32::Register temp = temps.Acquire(); + + __ Lsl(temp, second_hi, shift_value); + __ Orr(temp, temp, Operand(second_lo, ShiftType::LSR, 32 - shift_value)); + GenerateDataProc(kind, + out, + first, + Operand(second_lo, ShiftType::LSL, shift_value), + temp, + codegen); + } + } else { + DCHECK(shift == ShiftType::ASR || shift == ShiftType::LSR); + + // We are not doing this for HInstruction::kAdd because the output will require + // Location::kOutputOverlap; not applicable to other cases. + if (kind == HInstruction::kOr || kind == HInstruction::kXor) { + GenerateDataProcInstruction(kind, + out_lo, + first_lo, + Operand(second_lo, ShiftType::LSR, shift_value), + codegen); + GenerateDataProcInstruction(kind, + out_lo, + out_lo, + Operand(second_hi, ShiftType::LSL, 32 - shift_value), + codegen); + GenerateDataProcInstruction(kind, + out_hi, + first_hi, + Operand(second_hi, shift, shift_value), + codegen); + } else { + const vixl32::Register temp = temps.Acquire(); + + __ Lsr(temp, second_lo, shift_value); + __ Orr(temp, temp, Operand(second_hi, ShiftType::LSL, 32 - shift_value)); + GenerateDataProc(kind, + out, + first, + temp, + Operand(second_hi, shift, shift_value), + codegen); + } + } + } +} + #undef __ CodeGeneratorARMVIXL::CodeGeneratorARMVIXL(HGraph* graph, @@ -6781,6 +6971,60 @@ void InstructionCodeGeneratorARMVIXL::VisitBitwiseNegatedRight(HBitwiseNegatedRi } } +void LocationsBuilderARMVIXL::VisitDataProcWithShifterOp( + HDataProcWithShifterOp* instruction) { + DCHECK(instruction->GetType() == Primitive::kPrimInt || + instruction->GetType() == Primitive::kPrimLong); + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + const bool overlap = instruction->GetType() == Primitive::kPrimLong && + HDataProcWithShifterOp::IsExtensionOp(instruction->GetOpKind()); + + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), + overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorARMVIXL::VisitDataProcWithShifterOp( + HDataProcWithShifterOp* instruction) { + const LocationSummary* const locations = instruction->GetLocations(); + const HInstruction::InstructionKind kind = instruction->GetInstrKind(); + const HDataProcWithShifterOp::OpKind op_kind = instruction->GetOpKind(); + + if (instruction->GetType() == Primitive::kPrimInt) { + DCHECK(!HDataProcWithShifterOp::IsExtensionOp(op_kind)); + + const vixl32::Register second = instruction->InputAt(1)->GetType() == Primitive::kPrimLong + ? LowRegisterFrom(locations->InAt(1)) + : InputRegisterAt(instruction, 1); + + GenerateDataProcInstruction(kind, + OutputRegister(instruction), + InputRegisterAt(instruction, 0), + Operand(second, + ShiftFromOpKind(op_kind), + instruction->GetShiftAmount()), + codegen_); + } else { + DCHECK_EQ(instruction->GetType(), Primitive::kPrimLong); + + if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) { + const vixl32::Register second = InputRegisterAt(instruction, 1); + + DCHECK(!LowRegisterFrom(locations->Out()).Is(second)); + GenerateDataProc(kind, + locations->Out(), + locations->InAt(0), + second, + Operand(second, ShiftType::ASR, 31), + codegen_); + } else { + GenerateLongDataProc(instruction, codegen_); + } + } +} + // TODO(VIXL): Remove optimizations in the helper when they are implemented in vixl. void InstructionCodeGeneratorARMVIXL::GenerateAndConst(vixl32::Register out, vixl32::Register first, diff --git a/compiler/optimizing/common_arm.h b/compiler/optimizing/common_arm.h index ecb86875d6..e184745520 100644 --- a/compiler/optimizing/common_arm.h +++ b/compiler/optimizing/common_arm.h @@ -17,6 +17,7 @@ #ifndef ART_COMPILER_OPTIMIZING_COMMON_ARM_H_ #define ART_COMPILER_OPTIMIZING_COMMON_ARM_H_ +#include "instruction_simplifier_shared.h" #include "debug/dwarf/register.h" #include "locations.h" #include "nodes.h" @@ -29,6 +30,9 @@ #pragma GCC diagnostic pop namespace art { + +using helpers::HasShifterOperand; + namespace arm { namespace helpers { @@ -218,6 +222,14 @@ inline Location LocationFrom(const vixl::aarch32::SRegister& low, return Location::FpuRegisterPairLocation(low.GetCode(), high.GetCode()); } +inline bool ShifterOperandSupportsExtension(HInstruction* instruction) { + DCHECK(HasShifterOperand(instruction, kArm)); + // TODO: HAdd applied to the other integral types could make use of + // the SXTAB, SXTAH, UXTAB and UXTAH instructions. + return instruction->GetType() == Primitive::kPrimLong && + (instruction->IsAdd() || instruction->IsSub()); +} + } // namespace helpers } // namespace arm } // namespace art diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h index 93ea090583..d3f431e327 100644 --- a/compiler/optimizing/common_arm64.h +++ b/compiler/optimizing/common_arm64.h @@ -18,6 +18,7 @@ #define ART_COMPILER_OPTIMIZING_COMMON_ARM64_H_ #include "code_generator.h" +#include "instruction_simplifier_shared.h" #include "locations.h" #include "nodes.h" #include "utils/arm64/assembler_arm64.h" @@ -31,6 +32,10 @@ #pragma GCC diagnostic pop namespace art { + +using helpers::CanFitInShifterOperand; +using helpers::HasShifterOperand; + namespace arm64 { namespace helpers { @@ -290,11 +295,11 @@ inline bool ArtVixlRegCodeCoherentForRegSet(uint32_t art_core_registers, return true; } -inline vixl::aarch64::Shift ShiftFromOpKind(HArm64DataProcWithShifterOp::OpKind op_kind) { +inline vixl::aarch64::Shift ShiftFromOpKind(HDataProcWithShifterOp::OpKind op_kind) { switch (op_kind) { - case HArm64DataProcWithShifterOp::kASR: return vixl::aarch64::ASR; - case HArm64DataProcWithShifterOp::kLSL: return vixl::aarch64::LSL; - case HArm64DataProcWithShifterOp::kLSR: return vixl::aarch64::LSR; + case HDataProcWithShifterOp::kASR: return vixl::aarch64::ASR; + case HDataProcWithShifterOp::kLSL: return vixl::aarch64::LSL; + case HDataProcWithShifterOp::kLSR: return vixl::aarch64::LSR; default: LOG(FATAL) << "Unexpected op kind " << op_kind; UNREACHABLE(); @@ -302,14 +307,14 @@ inline vixl::aarch64::Shift ShiftFromOpKind(HArm64DataProcWithShifterOp::OpKind } } -inline vixl::aarch64::Extend ExtendFromOpKind(HArm64DataProcWithShifterOp::OpKind op_kind) { +inline vixl::aarch64::Extend ExtendFromOpKind(HDataProcWithShifterOp::OpKind op_kind) { switch (op_kind) { - case HArm64DataProcWithShifterOp::kUXTB: return vixl::aarch64::UXTB; - case HArm64DataProcWithShifterOp::kUXTH: return vixl::aarch64::UXTH; - case HArm64DataProcWithShifterOp::kUXTW: return vixl::aarch64::UXTW; - case HArm64DataProcWithShifterOp::kSXTB: return vixl::aarch64::SXTB; - case HArm64DataProcWithShifterOp::kSXTH: return vixl::aarch64::SXTH; - case HArm64DataProcWithShifterOp::kSXTW: return vixl::aarch64::SXTW; + case HDataProcWithShifterOp::kUXTB: return vixl::aarch64::UXTB; + case HDataProcWithShifterOp::kUXTH: return vixl::aarch64::UXTH; + case HDataProcWithShifterOp::kUXTW: return vixl::aarch64::UXTW; + case HDataProcWithShifterOp::kSXTB: return vixl::aarch64::SXTB; + case HDataProcWithShifterOp::kSXTH: return vixl::aarch64::SXTH; + case HDataProcWithShifterOp::kSXTW: return vixl::aarch64::SXTW; default: LOG(FATAL) << "Unexpected op kind " << op_kind; UNREACHABLE(); @@ -317,31 +322,8 @@ inline vixl::aarch64::Extend ExtendFromOpKind(HArm64DataProcWithShifterOp::OpKin } } -inline bool CanFitInShifterOperand(HInstruction* instruction) { - if (instruction->IsTypeConversion()) { - HTypeConversion* conversion = instruction->AsTypeConversion(); - Primitive::Type result_type = conversion->GetResultType(); - Primitive::Type input_type = conversion->GetInputType(); - // We don't expect to see the same type as input and result. - return Primitive::IsIntegralType(result_type) && Primitive::IsIntegralType(input_type) && - (result_type != input_type); - } else { - return (instruction->IsShl() && instruction->AsShl()->InputAt(1)->IsIntConstant()) || - (instruction->IsShr() && instruction->AsShr()->InputAt(1)->IsIntConstant()) || - (instruction->IsUShr() && instruction->AsUShr()->InputAt(1)->IsIntConstant()); - } -} - -inline bool HasShifterOperand(HInstruction* instr) { - // `neg` instructions are an alias of `sub` using the zero register as the - // first register input. - bool res = instr->IsAdd() || instr->IsAnd() || instr->IsNeg() || - instr->IsOr() || instr->IsSub() || instr->IsXor(); - return res; -} - inline bool ShifterOperandSupportsExtension(HInstruction* instruction) { - DCHECK(HasShifterOperand(instruction)); + DCHECK(HasShifterOperand(instruction, kArm64)); // Although the `neg` instruction is an alias of the `sub` instruction, `HNeg` // does *not* support extension. This is because the `extended register` form // of the `sub` instruction interprets the left register with code 31 as the diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index f6fba883bd..2bf5c53e17 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -511,12 +511,10 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { void VisitBitwiseNegatedRight(HBitwiseNegatedRight* instruction) OVERRIDE { StartAttributeStream("kind") << instruction->GetOpKind(); } -#endif -#ifdef ART_ENABLE_CODEGEN_arm64 - void VisitArm64DataProcWithShifterOp(HArm64DataProcWithShifterOp* instruction) OVERRIDE { + void VisitDataProcWithShifterOp(HDataProcWithShifterOp* instruction) OVERRIDE { StartAttributeStream("kind") << instruction->GetInstrKind() << "+" << instruction->GetOpKind(); - if (HArm64DataProcWithShifterOp::IsShiftOp(instruction->GetOpKind())) { + if (HDataProcWithShifterOp::IsShiftOp(instruction->GetOpKind())) { StartAttributeStream("shift") << instruction->GetShiftAmount(); } } diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc index 56e4c7a9c2..5f5e29b024 100644 --- a/compiler/optimizing/instruction_simplifier_arm.cc +++ b/compiler/optimizing/instruction_simplifier_arm.cc @@ -15,23 +15,124 @@ */ #include "code_generator.h" +#include "common_arm.h" #include "instruction_simplifier_arm.h" #include "instruction_simplifier_shared.h" #include "mirror/array-inl.h" +#include "nodes.h" namespace art { + +using helpers::CanFitInShifterOperand; +using helpers::HasShifterOperand; + namespace arm { -void InstructionSimplifierArmVisitor::VisitMul(HMul* instruction) { - if (TryCombineMultiplyAccumulate(instruction, kArm)) { +using helpers::ShifterOperandSupportsExtension; + +bool InstructionSimplifierArmVisitor::TryMergeIntoShifterOperand(HInstruction* use, + HInstruction* bitfield_op, + bool do_merge) { + DCHECK(HasShifterOperand(use, kArm)); + DCHECK(use->IsBinaryOperation()); + DCHECK(CanFitInShifterOperand(bitfield_op)); + DCHECK(!bitfield_op->HasEnvironmentUses()); + + Primitive::Type type = use->GetType(); + if (type != Primitive::kPrimInt && type != Primitive::kPrimLong) { + return false; + } + + HInstruction* left = use->InputAt(0); + HInstruction* right = use->InputAt(1); + DCHECK(left == bitfield_op || right == bitfield_op); + + if (left == right) { + // TODO: Handle special transformations in this situation? + // For example should we transform `(x << 1) + (x << 1)` into `(x << 2)`? + // Or should this be part of a separate transformation logic? + return false; + } + + bool is_commutative = use->AsBinaryOperation()->IsCommutative(); + HInstruction* other_input; + if (bitfield_op == right) { + other_input = left; + } else { + if (is_commutative) { + other_input = right; + } else { + return false; + } + } + + HDataProcWithShifterOp::OpKind op_kind; + int shift_amount = 0; + + HDataProcWithShifterOp::GetOpInfoFromInstruction(bitfield_op, &op_kind, &shift_amount); + shift_amount &= use->GetType() == Primitive::kPrimInt + ? kMaxIntShiftDistance + : kMaxLongShiftDistance; + + if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) { + if (!ShifterOperandSupportsExtension(use)) { + return false; + } + // Shift by 1 is a special case that results in the same number and type of instructions + // as this simplification, but potentially shorter code. + } else if (type == Primitive::kPrimLong && shift_amount == 1) { + return false; + } + + if (do_merge) { + HDataProcWithShifterOp* alu_with_op = + new (GetGraph()->GetArena()) HDataProcWithShifterOp(use, + other_input, + bitfield_op->InputAt(0), + op_kind, + shift_amount, + use->GetDexPc()); + use->GetBlock()->ReplaceAndRemoveInstructionWith(use, alu_with_op); + if (bitfield_op->GetUses().empty()) { + bitfield_op->GetBlock()->RemoveInstruction(bitfield_op); + } RecordSimplification(); } + + return true; } -void InstructionSimplifierArmVisitor::VisitOr(HOr* instruction) { - if (TryMergeNegatedInput(instruction)) { - RecordSimplification(); +// Merge a bitfield move instruction into its uses if it can be merged in all of them. +bool InstructionSimplifierArmVisitor::TryMergeIntoUsersShifterOperand(HInstruction* bitfield_op) { + DCHECK(CanFitInShifterOperand(bitfield_op)); + + if (bitfield_op->HasEnvironmentUses()) { + return false; + } + + const HUseList<HInstruction*>& uses = bitfield_op->GetUses(); + + // Check whether we can merge the instruction in all its users' shifter operand. + for (const HUseListNode<HInstruction*>& use : uses) { + HInstruction* user = use.GetUser(); + if (!HasShifterOperand(user, kArm)) { + return false; + } + if (!CanMergeIntoShifterOperand(user, bitfield_op)) { + return false; + } } + + // Merge the instruction into its uses. + for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) { + HInstruction* user = it->GetUser(); + // Increment `it` now because `*it` will disappear thanks to MergeIntoShifterOperand(). + ++it; + bool merged = MergeIntoShifterOperand(user, bitfield_op); + DCHECK(merged); + } + + return true; } void InstructionSimplifierArmVisitor::VisitAnd(HAnd* instruction) { @@ -89,5 +190,49 @@ void InstructionSimplifierArmVisitor::VisitArraySet(HArraySet* instruction) { } } +void InstructionSimplifierArmVisitor::VisitMul(HMul* instruction) { + if (TryCombineMultiplyAccumulate(instruction, kArm)) { + RecordSimplification(); + } +} + +void InstructionSimplifierArmVisitor::VisitOr(HOr* instruction) { + if (TryMergeNegatedInput(instruction)) { + RecordSimplification(); + } +} + +void InstructionSimplifierArmVisitor::VisitShl(HShl* instruction) { + if (instruction->InputAt(1)->IsConstant()) { + TryMergeIntoUsersShifterOperand(instruction); + } +} + +void InstructionSimplifierArmVisitor::VisitShr(HShr* instruction) { + if (instruction->InputAt(1)->IsConstant()) { + TryMergeIntoUsersShifterOperand(instruction); + } +} + +void InstructionSimplifierArmVisitor::VisitTypeConversion(HTypeConversion* instruction) { + Primitive::Type result_type = instruction->GetResultType(); + Primitive::Type input_type = instruction->GetInputType(); + + if (input_type == result_type) { + // We let the arch-independent code handle this. + return; + } + + if (Primitive::IsIntegralType(result_type) && Primitive::IsIntegralType(input_type)) { + TryMergeIntoUsersShifterOperand(instruction); + } +} + +void InstructionSimplifierArmVisitor::VisitUShr(HUShr* instruction) { + if (instruction->InputAt(1)->IsConstant()) { + TryMergeIntoUsersShifterOperand(instruction); + } +} + } // namespace arm } // namespace art diff --git a/compiler/optimizing/instruction_simplifier_arm.h b/compiler/optimizing/instruction_simplifier_arm.h index 9b54511340..e2ed257777 100644 --- a/compiler/optimizing/instruction_simplifier_arm.h +++ b/compiler/optimizing/instruction_simplifier_arm.h @@ -35,11 +35,41 @@ class InstructionSimplifierArmVisitor : public HGraphVisitor { } } - void VisitMul(HMul* instruction) OVERRIDE; - void VisitOr(HOr* instruction) OVERRIDE; + bool TryMergeIntoUsersShifterOperand(HInstruction* instruction); + bool TryMergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op, bool do_merge); + bool CanMergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op) { + return TryMergeIntoShifterOperand(use, bitfield_op, /* do_merge */ false); + } + bool MergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op) { + DCHECK(CanMergeIntoShifterOperand(use, bitfield_op)); + return TryMergeIntoShifterOperand(use, bitfield_op, /* do_merge */ true); + } + + /** + * This simplifier uses a special-purpose BB visitor. + * (1) No need to visit Phi nodes. + * (2) Since statements can be removed in a "forward" fashion, + * the visitor should test if each statement is still there. + */ + void VisitBasicBlock(HBasicBlock* block) OVERRIDE { + // TODO: fragile iteration, provide more robust iterators? + for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { + HInstruction* instruction = it.Current(); + if (instruction->IsInBlock()) { + instruction->Accept(this); + } + } + } + void VisitAnd(HAnd* instruction) OVERRIDE; void VisitArrayGet(HArrayGet* instruction) OVERRIDE; void VisitArraySet(HArraySet* instruction) OVERRIDE; + void VisitMul(HMul* instruction) OVERRIDE; + void VisitOr(HOr* instruction) OVERRIDE; + void VisitShl(HShl* instruction) OVERRIDE; + void VisitShr(HShr* instruction) OVERRIDE; + void VisitTypeConversion(HTypeConversion* instruction) OVERRIDE; + void VisitUShr(HUShr* instruction) OVERRIDE; OptimizingCompilerStats* stats_; }; diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc index 6d107d571f..73b7b2bd95 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.cc +++ b/compiler/optimizing/instruction_simplifier_arm64.cc @@ -22,16 +22,18 @@ #include "mirror/string.h" namespace art { -namespace arm64 { using helpers::CanFitInShifterOperand; using helpers::HasShifterOperand; + +namespace arm64 { + using helpers::ShifterOperandSupportsExtension; bool InstructionSimplifierArm64Visitor::TryMergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op, bool do_merge) { - DCHECK(HasShifterOperand(use)); + DCHECK(HasShifterOperand(use, kArm64)); DCHECK(use->IsBinaryOperation() || use->IsNeg()); DCHECK(CanFitInShifterOperand(bitfield_op)); DCHECK(!bitfield_op->HasEnvironmentUses()); @@ -72,23 +74,22 @@ bool InstructionSimplifierArm64Visitor::TryMergeIntoShifterOperand(HInstruction* } } - HArm64DataProcWithShifterOp::OpKind op_kind; + HDataProcWithShifterOp::OpKind op_kind; int shift_amount = 0; - HArm64DataProcWithShifterOp::GetOpInfoFromInstruction(bitfield_op, &op_kind, &shift_amount); + HDataProcWithShifterOp::GetOpInfoFromInstruction(bitfield_op, &op_kind, &shift_amount); - if (HArm64DataProcWithShifterOp::IsExtensionOp(op_kind) && - !ShifterOperandSupportsExtension(use)) { + if (HDataProcWithShifterOp::IsExtensionOp(op_kind) && !ShifterOperandSupportsExtension(use)) { return false; } if (do_merge) { - HArm64DataProcWithShifterOp* alu_with_op = - new (GetGraph()->GetArena()) HArm64DataProcWithShifterOp(use, - other_input, - bitfield_op->InputAt(0), - op_kind, - shift_amount, - use->GetDexPc()); + HDataProcWithShifterOp* alu_with_op = + new (GetGraph()->GetArena()) HDataProcWithShifterOp(use, + other_input, + bitfield_op->InputAt(0), + op_kind, + shift_amount, + use->GetDexPc()); use->GetBlock()->ReplaceAndRemoveInstructionWith(use, alu_with_op); if (bitfield_op->GetUses().empty()) { bitfield_op->GetBlock()->RemoveInstruction(bitfield_op); @@ -112,7 +113,7 @@ bool InstructionSimplifierArm64Visitor::TryMergeIntoUsersShifterOperand(HInstruc // Check whether we can merge the instruction in all its users' shifter operand. for (const HUseListNode<HInstruction*>& use : uses) { HInstruction* user = use.GetUser(); - if (!HasShifterOperand(user)) { + if (!HasShifterOperand(user, kArm64)) { return false; } if (!CanMergeIntoShifterOperand(user, bitfield_op)) { diff --git a/compiler/optimizing/instruction_simplifier_arm64.h b/compiler/optimizing/instruction_simplifier_arm64.h index d4cb1f14b7..65654f50f4 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.h +++ b/compiler/optimizing/instruction_simplifier_arm64.h @@ -40,11 +40,11 @@ class InstructionSimplifierArm64Visitor : public HGraphVisitor { HInstruction* bitfield_op, bool do_merge); bool CanMergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op) { - return TryMergeIntoShifterOperand(use, bitfield_op, false); + return TryMergeIntoShifterOperand(use, bitfield_op, /* do_merge */ false); } bool MergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op) { DCHECK(CanMergeIntoShifterOperand(use, bitfield_op)); - return TryMergeIntoShifterOperand(use, bitfield_op, true); + return TryMergeIntoShifterOperand(use, bitfield_op, /* do_merge */ true); } /** diff --git a/compiler/optimizing/instruction_simplifier_shared.h b/compiler/optimizing/instruction_simplifier_shared.h index 56804f5e90..83e3ffca57 100644 --- a/compiler/optimizing/instruction_simplifier_shared.h +++ b/compiler/optimizing/instruction_simplifier_shared.h @@ -21,6 +21,33 @@ namespace art { +namespace helpers { + +inline bool CanFitInShifterOperand(HInstruction* instruction) { + if (instruction->IsTypeConversion()) { + HTypeConversion* conversion = instruction->AsTypeConversion(); + Primitive::Type result_type = conversion->GetResultType(); + Primitive::Type input_type = conversion->GetInputType(); + // We don't expect to see the same type as input and result. + return Primitive::IsIntegralType(result_type) && Primitive::IsIntegralType(input_type) && + (result_type != input_type); + } else { + return (instruction->IsShl() && instruction->AsShl()->InputAt(1)->IsIntConstant()) || + (instruction->IsShr() && instruction->AsShr()->InputAt(1)->IsIntConstant()) || + (instruction->IsUShr() && instruction->AsUShr()->InputAt(1)->IsIntConstant()); + } +} + +inline bool HasShifterOperand(HInstruction* instr, InstructionSet isa) { + // On ARM64 `neg` instructions are an alias of `sub` using the zero register + // as the first register input. + bool res = instr->IsAdd() || instr->IsAnd() || (isa == kArm64 && instr->IsNeg()) || + instr->IsOr() || instr->IsSub() || instr->IsXor(); + return res; +} + +} // namespace helpers + bool TryCombineMultiplyAccumulate(HMul* mul, InstructionSet isa); // For bitwise operations (And/Or/Xor) with a negated input, try to use // a negated bitwise instruction. diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 96f9abafbf..56ca84a03f 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1346,6 +1346,7 @@ class HLoopInformationOutwardIterator : public ValueObject { #else #define FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M) \ M(BitwiseNegatedRight, Instruction) \ + M(DataProcWithShifterOp, Instruction) \ M(MultiplyAccumulate, Instruction) \ M(IntermediateAddress, Instruction) #endif @@ -1357,12 +1358,7 @@ class HLoopInformationOutwardIterator : public ValueObject { M(ArmDexCacheArraysBase, Instruction) #endif -#ifndef ART_ENABLE_CODEGEN_arm64 #define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) -#else -#define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) \ - M(Arm64DataProcWithShifterOp, Instruction) -#endif #ifndef ART_ENABLE_CODEGEN_mips #define FOR_EACH_CONCRETE_INSTRUCTION_MIPS(M) @@ -6603,9 +6599,6 @@ class HParallelMove FINAL : public HTemplateInstruction<0> { #ifdef ART_ENABLE_CODEGEN_arm #include "nodes_arm.h" #endif -#ifdef ART_ENABLE_CODEGEN_arm64 -#include "nodes_arm64.h" -#endif #ifdef ART_ENABLE_CODEGEN_mips #include "nodes_mips.h" #endif diff --git a/compiler/optimizing/nodes_arm64.h b/compiler/optimizing/nodes_arm64.h deleted file mode 100644 index 3f88717c2a..0000000000 --- a/compiler/optimizing/nodes_arm64.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_OPTIMIZING_NODES_ARM64_H_ -#define ART_COMPILER_OPTIMIZING_NODES_ARM64_H_ - -#include "nodes.h" - -namespace art { - -class HArm64DataProcWithShifterOp FINAL : public HExpression<2> { - public: - enum OpKind { - kLSL, // Logical shift left. - kLSR, // Logical shift right. - kASR, // Arithmetic shift right. - kUXTB, // Unsigned extend byte. - kUXTH, // Unsigned extend half-word. - kUXTW, // Unsigned extend word. - kSXTB, // Signed extend byte. - kSXTH, // Signed extend half-word. - kSXTW, // Signed extend word. - - // Aliases. - kFirstShiftOp = kLSL, - kLastShiftOp = kASR, - kFirstExtensionOp = kUXTB, - kLastExtensionOp = kSXTW - }; - HArm64DataProcWithShifterOp(HInstruction* instr, - HInstruction* left, - HInstruction* right, - OpKind op, - // The shift argument is unused if the operation - // is an extension. - int shift = 0, - uint32_t dex_pc = kNoDexPc) - : HExpression(instr->GetType(), SideEffects::None(), dex_pc), - instr_kind_(instr->GetKind()), op_kind_(op), shift_amount_(shift) { - DCHECK(!instr->HasSideEffects()); - SetRawInputAt(0, left); - SetRawInputAt(1, right); - } - - bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(const HInstruction* other_instr) const OVERRIDE { - const HArm64DataProcWithShifterOp* other = other_instr->AsArm64DataProcWithShifterOp(); - return instr_kind_ == other->instr_kind_ && - op_kind_ == other->op_kind_ && - shift_amount_ == other->shift_amount_; - } - - static bool IsShiftOp(OpKind op_kind) { - return kFirstShiftOp <= op_kind && op_kind <= kLastShiftOp; - } - - static bool IsExtensionOp(OpKind op_kind) { - return kFirstExtensionOp <= op_kind && op_kind <= kLastExtensionOp; - } - - // Find the operation kind and shift amount from a bitfield move instruction. - static void GetOpInfoFromInstruction(HInstruction* bitfield_op, - /*out*/OpKind* op_kind, - /*out*/int* shift_amount); - - InstructionKind GetInstrKind() const { return instr_kind_; } - OpKind GetOpKind() const { return op_kind_; } - int GetShiftAmount() const { return shift_amount_; } - - DECLARE_INSTRUCTION(Arm64DataProcWithShifterOp); - - private: - InstructionKind instr_kind_; - OpKind op_kind_; - int shift_amount_; - - friend std::ostream& operator<<(std::ostream& os, OpKind op); - - DISALLOW_COPY_AND_ASSIGN(HArm64DataProcWithShifterOp); -}; - -std::ostream& operator<<(std::ostream& os, const HArm64DataProcWithShifterOp::OpKind op); - -} // namespace art - -#endif // ART_COMPILER_OPTIMIZING_NODES_ARM64_H_ diff --git a/compiler/optimizing/nodes_arm64.cc b/compiler/optimizing/nodes_shared.cc index ac2f093847..f145bf9130 100644 --- a/compiler/optimizing/nodes_arm64.cc +++ b/compiler/optimizing/nodes_shared.cc @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 The Android Open Source Project + * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,15 +15,15 @@ */ #include "common_arm64.h" -#include "nodes.h" +#include "nodes_shared.h" namespace art { -using arm64::helpers::CanFitInShifterOperand; +using helpers::CanFitInShifterOperand; -void HArm64DataProcWithShifterOp::GetOpInfoFromInstruction(HInstruction* instruction, - /*out*/OpKind* op_kind, - /*out*/int* shift_amount) { +void HDataProcWithShifterOp::GetOpInfoFromInstruction(HInstruction* instruction, + /*out*/OpKind* op_kind, + /*out*/int* shift_amount) { DCHECK(CanFitInShifterOperand(instruction)); if (instruction->IsShl()) { *op_kind = kLSL; @@ -41,12 +41,11 @@ void HArm64DataProcWithShifterOp::GetOpInfoFromInstruction(HInstruction* instruc int result_size = Primitive::ComponentSize(result_type); int input_size = Primitive::ComponentSize(input_type); int min_size = std::min(result_size, input_size); - // This follows the logic in - // `InstructionCodeGeneratorARM64::VisitTypeConversion()`. if (result_type == Primitive::kPrimInt && input_type == Primitive::kPrimLong) { - // There is actually nothing to do. The register will be used as a W - // register, discarding the top bits. This is represented by the default - // encoding 'LSL 0'. + // There is actually nothing to do. On ARM the high register from the + // pair will be ignored. On ARM64 the register will be used as a W + // register, discarding the top bits. This is represented by the + // default encoding 'LSL 0'. *op_kind = kLSL; *shift_amount = 0; } else if (result_type == Primitive::kPrimChar || @@ -64,17 +63,17 @@ void HArm64DataProcWithShifterOp::GetOpInfoFromInstruction(HInstruction* instruc } } -std::ostream& operator<<(std::ostream& os, const HArm64DataProcWithShifterOp::OpKind op) { +std::ostream& operator<<(std::ostream& os, const HDataProcWithShifterOp::OpKind op) { switch (op) { - case HArm64DataProcWithShifterOp::kLSL: return os << "LSL"; - case HArm64DataProcWithShifterOp::kLSR: return os << "LSR"; - case HArm64DataProcWithShifterOp::kASR: return os << "ASR"; - case HArm64DataProcWithShifterOp::kUXTB: return os << "UXTB"; - case HArm64DataProcWithShifterOp::kUXTH: return os << "UXTH"; - case HArm64DataProcWithShifterOp::kUXTW: return os << "UXTW"; - case HArm64DataProcWithShifterOp::kSXTB: return os << "SXTB"; - case HArm64DataProcWithShifterOp::kSXTH: return os << "SXTH"; - case HArm64DataProcWithShifterOp::kSXTW: return os << "SXTW"; + case HDataProcWithShifterOp::kLSL: return os << "LSL"; + case HDataProcWithShifterOp::kLSR: return os << "LSR"; + case HDataProcWithShifterOp::kASR: return os << "ASR"; + case HDataProcWithShifterOp::kUXTB: return os << "UXTB"; + case HDataProcWithShifterOp::kUXTH: return os << "UXTH"; + case HDataProcWithShifterOp::kUXTW: return os << "UXTW"; + case HDataProcWithShifterOp::kSXTB: return os << "SXTB"; + case HDataProcWithShifterOp::kSXTH: return os << "SXTH"; + case HDataProcWithShifterOp::kSXTW: return os << "SXTW"; default: LOG(FATAL) << "Invalid OpKind " << static_cast<int>(op); UNREACHABLE(); diff --git a/compiler/optimizing/nodes_shared.h b/compiler/optimizing/nodes_shared.h index 814202e97b..c6bfbcc7fb 100644 --- a/compiler/optimizing/nodes_shared.h +++ b/compiler/optimizing/nodes_shared.h @@ -150,6 +150,81 @@ class HIntermediateAddress FINAL : public HExpression<2> { DISALLOW_COPY_AND_ASSIGN(HIntermediateAddress); }; +class HDataProcWithShifterOp FINAL : public HExpression<2> { + public: + enum OpKind { + kLSL, // Logical shift left. + kLSR, // Logical shift right. + kASR, // Arithmetic shift right. + kUXTB, // Unsigned extend byte. + kUXTH, // Unsigned extend half-word. + kUXTW, // Unsigned extend word. + kSXTB, // Signed extend byte. + kSXTH, // Signed extend half-word. + kSXTW, // Signed extend word. + + // Aliases. + kFirstShiftOp = kLSL, + kLastShiftOp = kASR, + kFirstExtensionOp = kUXTB, + kLastExtensionOp = kSXTW + }; + HDataProcWithShifterOp(HInstruction* instr, + HInstruction* left, + HInstruction* right, + OpKind op, + // The shift argument is unused if the operation + // is an extension. + int shift = 0, + uint32_t dex_pc = kNoDexPc) + : HExpression(instr->GetType(), SideEffects::None(), dex_pc), + instr_kind_(instr->GetKind()), op_kind_(op), + shift_amount_(shift & (instr->GetType() == Primitive::kPrimInt + ? kMaxIntShiftDistance + : kMaxLongShiftDistance)) { + DCHECK(!instr->HasSideEffects()); + SetRawInputAt(0, left); + SetRawInputAt(1, right); + } + + bool CanBeMoved() const OVERRIDE { return true; } + bool InstructionDataEquals(const HInstruction* other_instr) const OVERRIDE { + const HDataProcWithShifterOp* other = other_instr->AsDataProcWithShifterOp(); + return instr_kind_ == other->instr_kind_ && + op_kind_ == other->op_kind_ && + shift_amount_ == other->shift_amount_; + } + + static bool IsShiftOp(OpKind op_kind) { + return kFirstShiftOp <= op_kind && op_kind <= kLastShiftOp; + } + + static bool IsExtensionOp(OpKind op_kind) { + return kFirstExtensionOp <= op_kind && op_kind <= kLastExtensionOp; + } + + // Find the operation kind and shift amount from a bitfield move instruction. + static void GetOpInfoFromInstruction(HInstruction* bitfield_op, + /*out*/OpKind* op_kind, + /*out*/int* shift_amount); + + InstructionKind GetInstrKind() const { return instr_kind_; } + OpKind GetOpKind() const { return op_kind_; } + int GetShiftAmount() const { return shift_amount_; } + + DECLARE_INSTRUCTION(DataProcWithShifterOp); + + private: + InstructionKind instr_kind_; + OpKind op_kind_; + int shift_amount_; + + friend std::ostream& operator<<(std::ostream& os, OpKind op); + + DISALLOW_COPY_AND_ASSIGN(HDataProcWithShifterOp); +}; + +std::ostream& operator<<(std::ostream& os, const HDataProcWithShifterOp::OpKind op); } // namespace art diff --git a/compiler/optimizing/scheduler_arm64.cc b/compiler/optimizing/scheduler_arm64.cc index e3701fbcb1..558dcc4cbc 100644 --- a/compiler/optimizing/scheduler_arm64.cc +++ b/compiler/optimizing/scheduler_arm64.cc @@ -31,8 +31,8 @@ void SchedulingLatencyVisitorARM64::VisitBitwiseNegatedRight( last_visited_latency_ = kArm64IntegerOpLatency; } -void SchedulingLatencyVisitorARM64::VisitArm64DataProcWithShifterOp( - HArm64DataProcWithShifterOp* ATTRIBUTE_UNUSED) { +void SchedulingLatencyVisitorARM64::VisitDataProcWithShifterOp( + HDataProcWithShifterOp* ATTRIBUTE_UNUSED) { last_visited_latency_ = kArm64DataProcWithShifterOpLatency; } diff --git a/compiler/optimizing/scheduler_arm64.h b/compiler/optimizing/scheduler_arm64.h index 702027c535..7a33720655 100644 --- a/compiler/optimizing/scheduler_arm64.h +++ b/compiler/optimizing/scheduler_arm64.h @@ -74,7 +74,8 @@ class SchedulingLatencyVisitorARM64 : public SchedulingLatencyVisitor { #define FOR_EACH_SCHEDULED_SHARED_INSTRUCTION(M) \ M(BitwiseNegatedRight, unused) \ M(MultiplyAccumulate, unused) \ - M(IntermediateAddress, unused) + M(IntermediateAddress, unused) \ + M(DataProcWithShifterOp, unused) #define DECLARE_VISIT_INSTRUCTION(type, unused) \ void Visit##type(H##type* instruction) OVERRIDE; |