blob: 983d31d1688e2264fc5eb7dfd7b751b082f6b412 [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"
Artem Udovichenko4a0dad62016-01-26 12:28:31 +030020#include "instruction_simplifier_shared.h"
Alexandre Ramese6dbf482015-10-19 10:10:41 +010021#include "mirror/array-inl.h"
Vladimir Marko87f3fcb2016-04-28 15:52:11 +010022#include "mirror/string.h"
Alexandre Ramese6dbf482015-10-19 10:10:41 +010023
Alexandre Rames44b9cf92015-08-19 15:39:06 +010024namespace art {
25namespace arm64 {
26
Alexandre Rames8626b742015-11-25 16:28:08 +000027using helpers::CanFitInShifterOperand;
28using helpers::HasShifterOperand;
29using helpers::ShifterOperandSupportsExtension;
30
Alexandre Ramese6dbf482015-10-19 10:10:41 +010031void InstructionSimplifierArm64Visitor::TryExtractArrayAccessAddress(HInstruction* access,
32 HInstruction* array,
33 HInstruction* index,
Vladimir Marko87f3fcb2016-04-28 15:52:11 +010034 size_t data_offset) {
Roland Levillaincd3d0fb2016-01-15 19:26:48 +000035 if (kEmitCompilerReadBarrier) {
36 // The read barrier instrumentation does not support the
37 // HArm64IntermediateAddress instruction yet.
38 //
39 // TODO: Handle this case properly in the ARM64 code generator and
40 // re-enable this optimization; otherwise, remove this TODO.
41 // b/26601270
42 return;
43 }
Alexandre Ramese6dbf482015-10-19 10:10:41 +010044 if (index->IsConstant() ||
45 (index->IsBoundsCheck() && index->AsBoundsCheck()->GetIndex()->IsConstant())) {
46 // When the index is a constant all the addressing can be fitted in the
47 // memory access instruction, so do not split the access.
48 return;
49 }
50 if (access->IsArraySet() &&
51 access->AsArraySet()->GetValue()->GetType() == Primitive::kPrimNot) {
52 // The access may require a runtime call or the original array pointer.
53 return;
54 }
55
56 // Proceed to extract the base address computation.
57 ArenaAllocator* arena = GetGraph()->GetArena();
58
Vladimir Marko87f3fcb2016-04-28 15:52:11 +010059 HIntConstant* offset = GetGraph()->GetIntConstant(data_offset);
Alexandre Ramese6dbf482015-10-19 10:10:41 +010060 HArm64IntermediateAddress* address =
61 new (arena) HArm64IntermediateAddress(array, offset, kNoDexPc);
David Brazdil295abc12015-12-31 11:06:00 +000062 address->SetReferenceTypeInfo(array->GetReferenceTypeInfo());
Alexandre Ramese6dbf482015-10-19 10:10:41 +010063 access->GetBlock()->InsertInstructionBefore(address, access);
64 access->ReplaceInput(address, 0);
65 // Both instructions must depend on GC to prevent any instruction that can
66 // trigger GC to be inserted between the two.
67 access->AddSideEffects(SideEffects::DependsOnGC());
68 DCHECK(address->GetSideEffects().Includes(SideEffects::DependsOnGC()));
69 DCHECK(access->GetSideEffects().Includes(SideEffects::DependsOnGC()));
70 // TODO: Code generation for HArrayGet and HArraySet will check whether the input address
71 // is an HArm64IntermediateAddress and generate appropriate code.
72 // We would like to replace the `HArrayGet` and `HArraySet` with custom instructions (maybe
73 // `HArm64Load` and `HArm64Store`). We defer these changes because these new instructions would
74 // not bring any advantages yet.
75 // Also see the comments in
76 // `InstructionCodeGeneratorARM64::VisitArrayGet()` and
77 // `InstructionCodeGeneratorARM64::VisitArraySet()`.
78 RecordSimplification();
79}
80
Alexandre Rames8626b742015-11-25 16:28:08 +000081bool InstructionSimplifierArm64Visitor::TryMergeIntoShifterOperand(HInstruction* use,
82 HInstruction* bitfield_op,
83 bool do_merge) {
84 DCHECK(HasShifterOperand(use));
85 DCHECK(use->IsBinaryOperation() || use->IsNeg());
86 DCHECK(CanFitInShifterOperand(bitfield_op));
87 DCHECK(!bitfield_op->HasEnvironmentUses());
88
89 Primitive::Type type = use->GetType();
90 if (type != Primitive::kPrimInt && type != Primitive::kPrimLong) {
91 return false;
92 }
93
94 HInstruction* left;
95 HInstruction* right;
96 if (use->IsBinaryOperation()) {
97 left = use->InputAt(0);
98 right = use->InputAt(1);
99 } else {
100 DCHECK(use->IsNeg());
101 right = use->AsNeg()->InputAt(0);
102 left = GetGraph()->GetConstant(right->GetType(), 0);
103 }
104 DCHECK(left == bitfield_op || right == bitfield_op);
105
106 if (left == right) {
107 // TODO: Handle special transformations in this situation?
108 // For example should we transform `(x << 1) + (x << 1)` into `(x << 2)`?
109 // Or should this be part of a separate transformation logic?
110 return false;
111 }
112
113 bool is_commutative = use->IsBinaryOperation() && use->AsBinaryOperation()->IsCommutative();
114 HInstruction* other_input;
115 if (bitfield_op == right) {
116 other_input = left;
117 } else {
118 if (is_commutative) {
119 other_input = right;
120 } else {
121 return false;
122 }
123 }
124
125 HArm64DataProcWithShifterOp::OpKind op_kind;
126 int shift_amount = 0;
127 HArm64DataProcWithShifterOp::GetOpInfoFromInstruction(bitfield_op, &op_kind, &shift_amount);
128
129 if (HArm64DataProcWithShifterOp::IsExtensionOp(op_kind) &&
130 !ShifterOperandSupportsExtension(use)) {
131 return false;
132 }
133
134 if (do_merge) {
135 HArm64DataProcWithShifterOp* alu_with_op =
136 new (GetGraph()->GetArena()) HArm64DataProcWithShifterOp(use,
137 other_input,
138 bitfield_op->InputAt(0),
139 op_kind,
140 shift_amount,
141 use->GetDexPc());
142 use->GetBlock()->ReplaceAndRemoveInstructionWith(use, alu_with_op);
Vladimir Marko46817b82016-03-29 12:21:58 +0100143 if (bitfield_op->GetUses().empty()) {
Alexandre Rames8626b742015-11-25 16:28:08 +0000144 bitfield_op->GetBlock()->RemoveInstruction(bitfield_op);
145 }
146 RecordSimplification();
147 }
148
149 return true;
150}
151
152// Merge a bitfield move instruction into its uses if it can be merged in all of them.
153bool InstructionSimplifierArm64Visitor::TryMergeIntoUsersShifterOperand(HInstruction* bitfield_op) {
154 DCHECK(CanFitInShifterOperand(bitfield_op));
155
156 if (bitfield_op->HasEnvironmentUses()) {
157 return false;
158 }
159
160 const HUseList<HInstruction*>& uses = bitfield_op->GetUses();
161
162 // Check whether we can merge the instruction in all its users' shifter operand.
Vladimir Marko46817b82016-03-29 12:21:58 +0100163 for (const HUseListNode<HInstruction*>& use : uses) {
164 HInstruction* user = use.GetUser();
165 if (!HasShifterOperand(user)) {
Alexandre Rames8626b742015-11-25 16:28:08 +0000166 return false;
167 }
Vladimir Marko46817b82016-03-29 12:21:58 +0100168 if (!CanMergeIntoShifterOperand(user, bitfield_op)) {
Alexandre Rames8626b742015-11-25 16:28:08 +0000169 return false;
170 }
171 }
172
173 // Merge the instruction into its uses.
Vladimir Marko46817b82016-03-29 12:21:58 +0100174 for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
175 HInstruction* user = it->GetUser();
176 // Increment `it` now because `*it` will disappear thanks to MergeIntoShifterOperand().
177 ++it;
178 bool merged = MergeIntoShifterOperand(user, bitfield_op);
Alexandre Rames8626b742015-11-25 16:28:08 +0000179 DCHECK(merged);
180 }
181
182 return true;
183}
184
Kevin Brodsky9ff0d202016-01-11 13:43:31 +0000185void InstructionSimplifierArm64Visitor::VisitAnd(HAnd* instruction) {
Artem Serov7fc63502016-02-09 17:15:29 +0000186 if (TryMergeNegatedInput(instruction)) {
187 RecordSimplification();
188 }
Kevin Brodsky9ff0d202016-01-11 13:43:31 +0000189}
190
Alexandre Ramese6dbf482015-10-19 10:10:41 +0100191void InstructionSimplifierArm64Visitor::VisitArrayGet(HArrayGet* instruction) {
Vladimir Marko87f3fcb2016-04-28 15:52:11 +0100192 size_t data_offset = CodeGenerator::GetArrayDataOffset(instruction);
Alexandre Ramese6dbf482015-10-19 10:10:41 +0100193 TryExtractArrayAccessAddress(instruction,
194 instruction->GetArray(),
195 instruction->GetIndex(),
Vladimir Marko87f3fcb2016-04-28 15:52:11 +0100196 data_offset);
Alexandre Ramese6dbf482015-10-19 10:10:41 +0100197}
198
199void InstructionSimplifierArm64Visitor::VisitArraySet(HArraySet* instruction) {
Vladimir Marko87f3fcb2016-04-28 15:52:11 +0100200 size_t access_size = Primitive::ComponentSize(instruction->GetComponentType());
201 size_t data_offset = mirror::Array::DataOffset(access_size).Uint32Value();
Alexandre Ramese6dbf482015-10-19 10:10:41 +0100202 TryExtractArrayAccessAddress(instruction,
203 instruction->GetArray(),
204 instruction->GetIndex(),
Vladimir Marko87f3fcb2016-04-28 15:52:11 +0100205 data_offset);
Alexandre Ramese6dbf482015-10-19 10:10:41 +0100206}
207
Alexandre Rames418318f2015-11-20 15:55:47 +0000208void InstructionSimplifierArm64Visitor::VisitMul(HMul* instruction) {
Artem Udovichenko4a0dad62016-01-26 12:28:31 +0300209 if (TryCombineMultiplyAccumulate(instruction, kArm64)) {
210 RecordSimplification();
Alexandre Rames418318f2015-11-20 15:55:47 +0000211 }
212}
213
Kevin Brodsky9ff0d202016-01-11 13:43:31 +0000214void InstructionSimplifierArm64Visitor::VisitOr(HOr* instruction) {
Artem Serov7fc63502016-02-09 17:15:29 +0000215 if (TryMergeNegatedInput(instruction)) {
216 RecordSimplification();
217 }
Kevin Brodsky9ff0d202016-01-11 13:43:31 +0000218}
219
Alexandre Rames8626b742015-11-25 16:28:08 +0000220void InstructionSimplifierArm64Visitor::VisitShl(HShl* instruction) {
221 if (instruction->InputAt(1)->IsConstant()) {
222 TryMergeIntoUsersShifterOperand(instruction);
223 }
224}
225
226void InstructionSimplifierArm64Visitor::VisitShr(HShr* instruction) {
227 if (instruction->InputAt(1)->IsConstant()) {
228 TryMergeIntoUsersShifterOperand(instruction);
229 }
230}
231
232void InstructionSimplifierArm64Visitor::VisitTypeConversion(HTypeConversion* instruction) {
233 Primitive::Type result_type = instruction->GetResultType();
234 Primitive::Type input_type = instruction->GetInputType();
235
236 if (input_type == result_type) {
237 // We let the arch-independent code handle this.
238 return;
239 }
240
241 if (Primitive::IsIntegralType(result_type) && Primitive::IsIntegralType(input_type)) {
242 TryMergeIntoUsersShifterOperand(instruction);
243 }
244}
245
246void InstructionSimplifierArm64Visitor::VisitUShr(HUShr* instruction) {
247 if (instruction->InputAt(1)->IsConstant()) {
248 TryMergeIntoUsersShifterOperand(instruction);
249 }
250}
251
Kevin Brodsky9ff0d202016-01-11 13:43:31 +0000252void InstructionSimplifierArm64Visitor::VisitXor(HXor* instruction) {
Artem Serov7fc63502016-02-09 17:15:29 +0000253 if (TryMergeNegatedInput(instruction)) {
254 RecordSimplification();
255 }
Kevin Brodsky9ff0d202016-01-11 13:43:31 +0000256}
257
Alexandre Rames44b9cf92015-08-19 15:39:06 +0100258} // namespace arm64
259} // namespace art