blob: 6bbc751beec6a88bdf526f9fc2a640bb95a87337 [file] [log] [blame]
Alexandre Rames44b9cf92015-08-19 15:39:06 +01001/*
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
Alexandre Rames8626b742015-11-25 16:28:08 +000019#include "common_arm64.h"
Alexandre Ramese6dbf482015-10-19 10:10:41 +010020#include "mirror/array-inl.h"
21
Alexandre Rames44b9cf92015-08-19 15:39:06 +010022namespace art {
23namespace arm64 {
24
Alexandre Rames8626b742015-11-25 16:28:08 +000025using helpers::CanFitInShifterOperand;
26using helpers::HasShifterOperand;
27using helpers::ShifterOperandSupportsExtension;
28
Alexandre Ramese6dbf482015-10-19 10:10:41 +010029void InstructionSimplifierArm64Visitor::TryExtractArrayAccessAddress(HInstruction* access,
30 HInstruction* array,
31 HInstruction* index,
32 int access_size) {
33 if (index->IsConstant() ||
34 (index->IsBoundsCheck() && index->AsBoundsCheck()->GetIndex()->IsConstant())) {
35 // When the index is a constant all the addressing can be fitted in the
36 // memory access instruction, so do not split the access.
37 return;
38 }
39 if (access->IsArraySet() &&
40 access->AsArraySet()->GetValue()->GetType() == Primitive::kPrimNot) {
41 // The access may require a runtime call or the original array pointer.
42 return;
43 }
44
45 // Proceed to extract the base address computation.
46 ArenaAllocator* arena = GetGraph()->GetArena();
47
48 HIntConstant* offset =
49 GetGraph()->GetIntConstant(mirror::Array::DataOffset(access_size).Uint32Value());
50 HArm64IntermediateAddress* address =
51 new (arena) HArm64IntermediateAddress(array, offset, kNoDexPc);
David Brazdil295abc12015-12-31 11:06:00 +000052 address->SetReferenceTypeInfo(array->GetReferenceTypeInfo());
Alexandre Ramese6dbf482015-10-19 10:10:41 +010053 access->GetBlock()->InsertInstructionBefore(address, access);
54 access->ReplaceInput(address, 0);
55 // Both instructions must depend on GC to prevent any instruction that can
56 // trigger GC to be inserted between the two.
57 access->AddSideEffects(SideEffects::DependsOnGC());
58 DCHECK(address->GetSideEffects().Includes(SideEffects::DependsOnGC()));
59 DCHECK(access->GetSideEffects().Includes(SideEffects::DependsOnGC()));
60 // TODO: Code generation for HArrayGet and HArraySet will check whether the input address
61 // is an HArm64IntermediateAddress and generate appropriate code.
62 // We would like to replace the `HArrayGet` and `HArraySet` with custom instructions (maybe
63 // `HArm64Load` and `HArm64Store`). We defer these changes because these new instructions would
64 // not bring any advantages yet.
65 // Also see the comments in
66 // `InstructionCodeGeneratorARM64::VisitArrayGet()` and
67 // `InstructionCodeGeneratorARM64::VisitArraySet()`.
68 RecordSimplification();
69}
70
Alexandre Rames8626b742015-11-25 16:28:08 +000071bool InstructionSimplifierArm64Visitor::TryMergeIntoShifterOperand(HInstruction* use,
72 HInstruction* bitfield_op,
73 bool do_merge) {
74 DCHECK(HasShifterOperand(use));
75 DCHECK(use->IsBinaryOperation() || use->IsNeg());
76 DCHECK(CanFitInShifterOperand(bitfield_op));
77 DCHECK(!bitfield_op->HasEnvironmentUses());
78
79 Primitive::Type type = use->GetType();
80 if (type != Primitive::kPrimInt && type != Primitive::kPrimLong) {
81 return false;
82 }
83
84 HInstruction* left;
85 HInstruction* right;
86 if (use->IsBinaryOperation()) {
87 left = use->InputAt(0);
88 right = use->InputAt(1);
89 } else {
90 DCHECK(use->IsNeg());
91 right = use->AsNeg()->InputAt(0);
92 left = GetGraph()->GetConstant(right->GetType(), 0);
93 }
94 DCHECK(left == bitfield_op || right == bitfield_op);
95
96 if (left == right) {
97 // TODO: Handle special transformations in this situation?
98 // For example should we transform `(x << 1) + (x << 1)` into `(x << 2)`?
99 // Or should this be part of a separate transformation logic?
100 return false;
101 }
102
103 bool is_commutative = use->IsBinaryOperation() && use->AsBinaryOperation()->IsCommutative();
104 HInstruction* other_input;
105 if (bitfield_op == right) {
106 other_input = left;
107 } else {
108 if (is_commutative) {
109 other_input = right;
110 } else {
111 return false;
112 }
113 }
114
115 HArm64DataProcWithShifterOp::OpKind op_kind;
116 int shift_amount = 0;
117 HArm64DataProcWithShifterOp::GetOpInfoFromInstruction(bitfield_op, &op_kind, &shift_amount);
118
119 if (HArm64DataProcWithShifterOp::IsExtensionOp(op_kind) &&
120 !ShifterOperandSupportsExtension(use)) {
121 return false;
122 }
123
124 if (do_merge) {
125 HArm64DataProcWithShifterOp* alu_with_op =
126 new (GetGraph()->GetArena()) HArm64DataProcWithShifterOp(use,
127 other_input,
128 bitfield_op->InputAt(0),
129 op_kind,
130 shift_amount,
131 use->GetDexPc());
132 use->GetBlock()->ReplaceAndRemoveInstructionWith(use, alu_with_op);
133 if (bitfield_op->GetUses().IsEmpty()) {
134 bitfield_op->GetBlock()->RemoveInstruction(bitfield_op);
135 }
136 RecordSimplification();
137 }
138
139 return true;
140}
141
142// Merge a bitfield move instruction into its uses if it can be merged in all of them.
143bool InstructionSimplifierArm64Visitor::TryMergeIntoUsersShifterOperand(HInstruction* bitfield_op) {
144 DCHECK(CanFitInShifterOperand(bitfield_op));
145
146 if (bitfield_op->HasEnvironmentUses()) {
147 return false;
148 }
149
150 const HUseList<HInstruction*>& uses = bitfield_op->GetUses();
151
152 // Check whether we can merge the instruction in all its users' shifter operand.
153 for (HUseIterator<HInstruction*> it_use(uses); !it_use.Done(); it_use.Advance()) {
154 HInstruction* use = it_use.Current()->GetUser();
155 if (!HasShifterOperand(use)) {
156 return false;
157 }
158 if (!CanMergeIntoShifterOperand(use, bitfield_op)) {
159 return false;
160 }
161 }
162
163 // Merge the instruction into its uses.
164 for (HUseIterator<HInstruction*> it_use(uses); !it_use.Done(); it_use.Advance()) {
165 HInstruction* use = it_use.Current()->GetUser();
166 bool merged = MergeIntoShifterOperand(use, bitfield_op);
167 DCHECK(merged);
168 }
169
170 return true;
171}
172
Alexandre Rames418318f2015-11-20 15:55:47 +0000173bool InstructionSimplifierArm64Visitor::TrySimpleMultiplyAccumulatePatterns(
174 HMul* mul, HBinaryOperation* input_binop, HInstruction* input_other) {
175 DCHECK(Primitive::IsIntOrLongType(mul->GetType()));
176 DCHECK(input_binop->IsAdd() || input_binop->IsSub());
177 DCHECK_NE(input_binop, input_other);
178 if (!input_binop->HasOnlyOneNonEnvironmentUse()) {
179 return false;
180 }
181
182 // Try to interpret patterns like
183 // a * (b <+/-> 1)
184 // as
185 // (a * b) <+/-> a
186 HInstruction* input_a = input_other;
187 HInstruction* input_b = nullptr; // Set to a non-null value if we found a pattern to optimize.
188 HInstruction::InstructionKind op_kind;
189
190 if (input_binop->IsAdd()) {
191 if ((input_binop->GetConstantRight() != nullptr) && input_binop->GetConstantRight()->IsOne()) {
192 // Interpret
193 // a * (b + 1)
194 // as
195 // (a * b) + a
196 input_b = input_binop->GetLeastConstantLeft();
197 op_kind = HInstruction::kAdd;
198 }
199 } else {
200 DCHECK(input_binop->IsSub());
201 if (input_binop->GetRight()->IsConstant() &&
202 input_binop->GetRight()->AsConstant()->IsMinusOne()) {
203 // Interpret
204 // a * (b - (-1))
205 // as
206 // a + (a * b)
207 input_b = input_binop->GetLeft();
208 op_kind = HInstruction::kAdd;
209 } else if (input_binop->GetLeft()->IsConstant() &&
210 input_binop->GetLeft()->AsConstant()->IsOne()) {
211 // Interpret
212 // a * (1 - b)
213 // as
214 // a - (a * b)
215 input_b = input_binop->GetRight();
216 op_kind = HInstruction::kSub;
217 }
218 }
219
220 if (input_b == nullptr) {
221 // We did not find a pattern we can optimize.
222 return false;
223 }
224
225 HArm64MultiplyAccumulate* mulacc = new(GetGraph()->GetArena()) HArm64MultiplyAccumulate(
226 mul->GetType(), op_kind, input_a, input_a, input_b, mul->GetDexPc());
227
228 mul->GetBlock()->ReplaceAndRemoveInstructionWith(mul, mulacc);
229 input_binop->GetBlock()->RemoveInstruction(input_binop);
230
231 return false;
232}
233
Alexandre Ramese6dbf482015-10-19 10:10:41 +0100234void InstructionSimplifierArm64Visitor::VisitArrayGet(HArrayGet* instruction) {
235 TryExtractArrayAccessAddress(instruction,
236 instruction->GetArray(),
237 instruction->GetIndex(),
238 Primitive::ComponentSize(instruction->GetType()));
239}
240
241void InstructionSimplifierArm64Visitor::VisitArraySet(HArraySet* instruction) {
242 TryExtractArrayAccessAddress(instruction,
243 instruction->GetArray(),
244 instruction->GetIndex(),
245 Primitive::ComponentSize(instruction->GetComponentType()));
246}
247
Alexandre Rames418318f2015-11-20 15:55:47 +0000248void InstructionSimplifierArm64Visitor::VisitMul(HMul* instruction) {
249 Primitive::Type type = instruction->GetType();
250 if (!Primitive::IsIntOrLongType(type)) {
251 return;
252 }
253
254 HInstruction* use = instruction->HasNonEnvironmentUses()
255 ? instruction->GetUses().GetFirst()->GetUser()
256 : nullptr;
257
258 if (instruction->HasOnlyOneNonEnvironmentUse() && (use->IsAdd() || use->IsSub())) {
259 // Replace code looking like
260 // MUL tmp, x, y
261 // SUB dst, acc, tmp
262 // with
263 // MULSUB dst, acc, x, y
264 // Note that we do not want to (unconditionally) perform the merge when the
265 // multiplication has multiple uses and it can be merged in all of them.
266 // Multiple uses could happen on the same control-flow path, and we would
267 // then increase the amount of work. In the future we could try to evaluate
268 // whether all uses are on different control-flow paths (using dominance and
269 // reverse-dominance information) and only perform the merge when they are.
270 HInstruction* accumulator = nullptr;
271 HBinaryOperation* binop = use->AsBinaryOperation();
272 HInstruction* binop_left = binop->GetLeft();
273 HInstruction* binop_right = binop->GetRight();
274 // Be careful after GVN. This should not happen since the `HMul` has only
275 // one use.
276 DCHECK_NE(binop_left, binop_right);
277 if (binop_right == instruction) {
278 accumulator = binop_left;
279 } else if (use->IsAdd()) {
280 DCHECK_EQ(binop_left, instruction);
281 accumulator = binop_right;
282 }
283
284 if (accumulator != nullptr) {
285 HArm64MultiplyAccumulate* mulacc =
286 new (GetGraph()->GetArena()) HArm64MultiplyAccumulate(type,
287 binop->GetKind(),
288 accumulator,
289 instruction->GetLeft(),
290 instruction->GetRight());
291
292 binop->GetBlock()->ReplaceAndRemoveInstructionWith(binop, mulacc);
293 DCHECK(!instruction->HasUses());
294 instruction->GetBlock()->RemoveInstruction(instruction);
295 RecordSimplification();
296 return;
297 }
298 }
299
300 // Use multiply accumulate instruction for a few simple patterns.
301 // We prefer not applying the following transformations if the left and
302 // right inputs perform the same operation.
303 // We rely on GVN having squashed the inputs if appropriate. However the
304 // results are still correct even if that did not happen.
305 if (instruction->GetLeft() == instruction->GetRight()) {
306 return;
307 }
308
309 HInstruction* left = instruction->GetLeft();
310 HInstruction* right = instruction->GetRight();
311 if ((right->IsAdd() || right->IsSub()) &&
312 TrySimpleMultiplyAccumulatePatterns(instruction, right->AsBinaryOperation(), left)) {
313 return;
314 }
315 if ((left->IsAdd() || left->IsSub()) &&
316 TrySimpleMultiplyAccumulatePatterns(instruction, left->AsBinaryOperation(), right)) {
317 return;
318 }
319}
320
Alexandre Rames8626b742015-11-25 16:28:08 +0000321void InstructionSimplifierArm64Visitor::VisitShl(HShl* instruction) {
322 if (instruction->InputAt(1)->IsConstant()) {
323 TryMergeIntoUsersShifterOperand(instruction);
324 }
325}
326
327void InstructionSimplifierArm64Visitor::VisitShr(HShr* instruction) {
328 if (instruction->InputAt(1)->IsConstant()) {
329 TryMergeIntoUsersShifterOperand(instruction);
330 }
331}
332
333void InstructionSimplifierArm64Visitor::VisitTypeConversion(HTypeConversion* instruction) {
334 Primitive::Type result_type = instruction->GetResultType();
335 Primitive::Type input_type = instruction->GetInputType();
336
337 if (input_type == result_type) {
338 // We let the arch-independent code handle this.
339 return;
340 }
341
342 if (Primitive::IsIntegralType(result_type) && Primitive::IsIntegralType(input_type)) {
343 TryMergeIntoUsersShifterOperand(instruction);
344 }
345}
346
347void InstructionSimplifierArm64Visitor::VisitUShr(HUShr* instruction) {
348 if (instruction->InputAt(1)->IsConstant()) {
349 TryMergeIntoUsersShifterOperand(instruction);
350 }
351}
352
Alexandre Rames44b9cf92015-08-19 15:39:06 +0100353} // namespace arm64
354} // namespace art