1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "instruction_simplifier_arm64.h"
18 
19 #include "common_arm64.h"
20 #include "instruction_simplifier_shared.h"
21 #include "mirror/array-inl.h"
22 #include "mirror/string.h"
23 
24 namespace art {
25 
26 using helpers::CanFitInShifterOperand;
27 using helpers::HasShifterOperand;
28 
29 namespace arm64 {
30 
31 using helpers::ShifterOperandSupportsExtension;
32 
TryMergeIntoShifterOperand(HInstruction * use,HInstruction * bitfield_op,bool do_merge)33 bool InstructionSimplifierArm64Visitor::TryMergeIntoShifterOperand(HInstruction* use,
34                                                                    HInstruction* bitfield_op,
35                                                                    bool do_merge) {
36   DCHECK(HasShifterOperand(use, kArm64));
37   DCHECK(use->IsBinaryOperation() || use->IsNeg());
38   DCHECK(CanFitInShifterOperand(bitfield_op));
39   DCHECK(!bitfield_op->HasEnvironmentUses());
40 
41   Primitive::Type type = use->GetType();
42   if (type != Primitive::kPrimInt && type != Primitive::kPrimLong) {
43     return false;
44   }
45 
46   HInstruction* left;
47   HInstruction* right;
48   if (use->IsBinaryOperation()) {
49     left = use->InputAt(0);
50     right = use->InputAt(1);
51   } else {
52     DCHECK(use->IsNeg());
53     right = use->AsNeg()->InputAt(0);
54     left = GetGraph()->GetConstant(right->GetType(), 0);
55   }
56   DCHECK(left == bitfield_op || right == bitfield_op);
57 
58   if (left == right) {
59     // TODO: Handle special transformations in this situation?
60     // For example should we transform `(x << 1) + (x << 1)` into `(x << 2)`?
61     // Or should this be part of a separate transformation logic?
62     return false;
63   }
64 
65   bool is_commutative = use->IsBinaryOperation() && use->AsBinaryOperation()->IsCommutative();
66   HInstruction* other_input;
67   if (bitfield_op == right) {
68     other_input = left;
69   } else {
70     if (is_commutative) {
71       other_input = right;
72     } else {
73       return false;
74     }
75   }
76 
77   HDataProcWithShifterOp::OpKind op_kind;
78   int shift_amount = 0;
79   HDataProcWithShifterOp::GetOpInfoFromInstruction(bitfield_op, &op_kind, &shift_amount);
80 
81   if (HDataProcWithShifterOp::IsExtensionOp(op_kind) && !ShifterOperandSupportsExtension(use)) {
82     return false;
83   }
84 
85   if (do_merge) {
86     HDataProcWithShifterOp* alu_with_op =
87         new (GetGraph()->GetArena()) HDataProcWithShifterOp(use,
88                                                             other_input,
89                                                             bitfield_op->InputAt(0),
90                                                             op_kind,
91                                                             shift_amount,
92                                                             use->GetDexPc());
93     use->GetBlock()->ReplaceAndRemoveInstructionWith(use, alu_with_op);
94     if (bitfield_op->GetUses().empty()) {
95       bitfield_op->GetBlock()->RemoveInstruction(bitfield_op);
96     }
97     RecordSimplification();
98   }
99 
100   return true;
101 }
102 
103 // Merge a bitfield move instruction into its uses if it can be merged in all of them.
TryMergeIntoUsersShifterOperand(HInstruction * bitfield_op)104 bool InstructionSimplifierArm64Visitor::TryMergeIntoUsersShifterOperand(HInstruction* bitfield_op) {
105   DCHECK(CanFitInShifterOperand(bitfield_op));
106 
107   if (bitfield_op->HasEnvironmentUses()) {
108     return false;
109   }
110 
111   const HUseList<HInstruction*>& uses = bitfield_op->GetUses();
112 
113   // Check whether we can merge the instruction in all its users' shifter operand.
114   for (const HUseListNode<HInstruction*>& use : uses) {
115     HInstruction* user = use.GetUser();
116     if (!HasShifterOperand(user, kArm64)) {
117       return false;
118     }
119     if (!CanMergeIntoShifterOperand(user, bitfield_op)) {
120       return false;
121     }
122   }
123 
124   // Merge the instruction into its uses.
125   for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
126     HInstruction* user = it->GetUser();
127     // Increment `it` now because `*it` will disappear thanks to MergeIntoShifterOperand().
128     ++it;
129     bool merged = MergeIntoShifterOperand(user, bitfield_op);
130     DCHECK(merged);
131   }
132 
133   return true;
134 }
135 
VisitAnd(HAnd * instruction)136 void InstructionSimplifierArm64Visitor::VisitAnd(HAnd* instruction) {
137   if (TryMergeNegatedInput(instruction)) {
138     RecordSimplification();
139   }
140 }
141 
VisitArrayGet(HArrayGet * instruction)142 void InstructionSimplifierArm64Visitor::VisitArrayGet(HArrayGet* instruction) {
143   size_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
144   if (TryExtractArrayAccessAddress(instruction,
145                                    instruction->GetArray(),
146                                    instruction->GetIndex(),
147                                    data_offset)) {
148     RecordSimplification();
149   }
150 }
151 
VisitArraySet(HArraySet * instruction)152 void InstructionSimplifierArm64Visitor::VisitArraySet(HArraySet* instruction) {
153   size_t access_size = Primitive::ComponentSize(instruction->GetComponentType());
154   size_t data_offset = mirror::Array::DataOffset(access_size).Uint32Value();
155   if (TryExtractArrayAccessAddress(instruction,
156                                    instruction->GetArray(),
157                                    instruction->GetIndex(),
158                                    data_offset)) {
159     RecordSimplification();
160   }
161 }
162 
VisitMul(HMul * instruction)163 void InstructionSimplifierArm64Visitor::VisitMul(HMul* instruction) {
164   if (TryCombineMultiplyAccumulate(instruction, kArm64)) {
165     RecordSimplification();
166   }
167 }
168 
VisitOr(HOr * instruction)169 void InstructionSimplifierArm64Visitor::VisitOr(HOr* instruction) {
170   if (TryMergeNegatedInput(instruction)) {
171     RecordSimplification();
172   }
173 }
174 
VisitShl(HShl * instruction)175 void InstructionSimplifierArm64Visitor::VisitShl(HShl* instruction) {
176   if (instruction->InputAt(1)->IsConstant()) {
177     TryMergeIntoUsersShifterOperand(instruction);
178   }
179 }
180 
VisitShr(HShr * instruction)181 void InstructionSimplifierArm64Visitor::VisitShr(HShr* instruction) {
182   if (instruction->InputAt(1)->IsConstant()) {
183     TryMergeIntoUsersShifterOperand(instruction);
184   }
185 }
186 
VisitTypeConversion(HTypeConversion * instruction)187 void InstructionSimplifierArm64Visitor::VisitTypeConversion(HTypeConversion* instruction) {
188   Primitive::Type result_type = instruction->GetResultType();
189   Primitive::Type input_type = instruction->GetInputType();
190 
191   if (input_type == result_type) {
192     // We let the arch-independent code handle this.
193     return;
194   }
195 
196   if (Primitive::IsIntegralType(result_type) && Primitive::IsIntegralType(input_type)) {
197     TryMergeIntoUsersShifterOperand(instruction);
198   }
199 }
200 
VisitUShr(HUShr * instruction)201 void InstructionSimplifierArm64Visitor::VisitUShr(HUShr* instruction) {
202   if (instruction->InputAt(1)->IsConstant()) {
203     TryMergeIntoUsersShifterOperand(instruction);
204   }
205 }
206 
VisitXor(HXor * instruction)207 void InstructionSimplifierArm64Visitor::VisitXor(HXor* instruction) {
208   if (TryMergeNegatedInput(instruction)) {
209     RecordSimplification();
210   }
211 }
212 
VisitVecMul(HVecMul * instruction)213 void InstructionSimplifierArm64Visitor::VisitVecMul(HVecMul* instruction) {
214   if (TryCombineVecMultiplyAccumulate(instruction, kArm64)) {
215     RecordSimplification();
216   }
217 }
218 
219 }  // namespace arm64
220 }  // namespace art
221