ARM64: Use the shifter operands.
This introduces architecture-specific instruction simplification.
On ARM64 we try to merge shifts and sign-extension operations into
arithmetic and logical instructions.
For example for the Java code
int res = a + (b << 5);
we would generate
lsl w3, w2, #5
add w0, w1, w3
and we now generate
add w0, w1, w2, lsl #5
Change-Id: Ic03bdff44a1c12e21ddff1b0513bd32a730742b7
diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc
index 54dd2cc..6a34b13 100644
--- a/compiler/optimizing/instruction_simplifier_arm64.cc
+++ b/compiler/optimizing/instruction_simplifier_arm64.cc
@@ -16,11 +16,16 @@
#include "instruction_simplifier_arm64.h"
+#include "common_arm64.h"
#include "mirror/array-inl.h"
namespace art {
namespace arm64 {
+using helpers::CanFitInShifterOperand;
+using helpers::HasShifterOperand;
+using helpers::ShifterOperandSupportsExtension;
+
void InstructionSimplifierArm64Visitor::TryExtractArrayAccessAddress(HInstruction* access,
HInstruction* array,
HInstruction* index,
@@ -62,6 +67,108 @@
RecordSimplification();
}
+bool InstructionSimplifierArm64Visitor::TryMergeIntoShifterOperand(HInstruction* use,
+ HInstruction* bitfield_op,
+ bool do_merge) {
+ DCHECK(HasShifterOperand(use));
+ DCHECK(use->IsBinaryOperation() || use->IsNeg());
+ DCHECK(CanFitInShifterOperand(bitfield_op));
+ DCHECK(!bitfield_op->HasEnvironmentUses());
+
+ Primitive::Type type = use->GetType();
+ if (type != Primitive::kPrimInt && type != Primitive::kPrimLong) {
+ return false;
+ }
+
+ HInstruction* left;
+ HInstruction* right;
+ if (use->IsBinaryOperation()) {
+ left = use->InputAt(0);
+ right = use->InputAt(1);
+ } else {
+ DCHECK(use->IsNeg());
+ right = use->AsNeg()->InputAt(0);
+ left = GetGraph()->GetConstant(right->GetType(), 0);
+ }
+ 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->IsBinaryOperation() && use->AsBinaryOperation()->IsCommutative();
+ HInstruction* other_input;
+ if (bitfield_op == right) {
+ other_input = left;
+ } else {
+ if (is_commutative) {
+ other_input = right;
+ } else {
+ return false;
+ }
+ }
+
+ HArm64DataProcWithShifterOp::OpKind op_kind;
+ int shift_amount = 0;
+ HArm64DataProcWithShifterOp::GetOpInfoFromInstruction(bitfield_op, &op_kind, &shift_amount);
+
+ if (HArm64DataProcWithShifterOp::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());
+ use->GetBlock()->ReplaceAndRemoveInstructionWith(use, alu_with_op);
+ if (bitfield_op->GetUses().IsEmpty()) {
+ bitfield_op->GetBlock()->RemoveInstruction(bitfield_op);
+ }
+ RecordSimplification();
+ }
+
+ return true;
+}
+
+// Merge a bitfield move instruction into its uses if it can be merged in all of them.
+bool InstructionSimplifierArm64Visitor::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 (HUseIterator<HInstruction*> it_use(uses); !it_use.Done(); it_use.Advance()) {
+ HInstruction* use = it_use.Current()->GetUser();
+ if (!HasShifterOperand(use)) {
+ return false;
+ }
+ if (!CanMergeIntoShifterOperand(use, bitfield_op)) {
+ return false;
+ }
+ }
+
+ // Merge the instruction into its uses.
+ for (HUseIterator<HInstruction*> it_use(uses); !it_use.Done(); it_use.Advance()) {
+ HInstruction* use = it_use.Current()->GetUser();
+ bool merged = MergeIntoShifterOperand(use, bitfield_op);
+ DCHECK(merged);
+ }
+
+ return true;
+}
+
bool InstructionSimplifierArm64Visitor::TrySimpleMultiplyAccumulatePatterns(
HMul* mul, HBinaryOperation* input_binop, HInstruction* input_other) {
DCHECK(Primitive::IsIntOrLongType(mul->GetType()));
@@ -210,5 +317,37 @@
}
}
+void InstructionSimplifierArm64Visitor::VisitShl(HShl* instruction) {
+ if (instruction->InputAt(1)->IsConstant()) {
+ TryMergeIntoUsersShifterOperand(instruction);
+ }
+}
+
+void InstructionSimplifierArm64Visitor::VisitShr(HShr* instruction) {
+ if (instruction->InputAt(1)->IsConstant()) {
+ TryMergeIntoUsersShifterOperand(instruction);
+ }
+}
+
+void InstructionSimplifierArm64Visitor::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 InstructionSimplifierArm64Visitor::VisitUShr(HUShr* instruction) {
+ if (instruction->InputAt(1)->IsConstant()) {
+ TryMergeIntoUsersShifterOperand(instruction);
+ }
+}
+
} // namespace arm64
} // namespace art