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