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
24namespace art {
25
26using helpers::CanFitInShifterOperand;
27using helpers::HasShifterOperand;
28
29namespace arm64 {
30
31using helpers::ShifterOperandSupportsExtension;
32
33bool 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.
104bool 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
136void InstructionSimplifierArm64Visitor::VisitAnd(HAnd* instruction) {
137  if (TryMergeNegatedInput(instruction)) {
138    RecordSimplification();
139  }
140}
141
142void 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
152void 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
163void InstructionSimplifierArm64Visitor::VisitMul(HMul* instruction) {
164  if (TryCombineMultiplyAccumulate(instruction, kArm64)) {
165    RecordSimplification();
166  }
167}
168
169void InstructionSimplifierArm64Visitor::VisitOr(HOr* instruction) {
170  if (TryMergeNegatedInput(instruction)) {
171    RecordSimplification();
172  }
173}
174
175void InstructionSimplifierArm64Visitor::VisitShl(HShl* instruction) {
176  if (instruction->InputAt(1)->IsConstant()) {
177    TryMergeIntoUsersShifterOperand(instruction);
178  }
179}
180
181void InstructionSimplifierArm64Visitor::VisitShr(HShr* instruction) {
182  if (instruction->InputAt(1)->IsConstant()) {
183    TryMergeIntoUsersShifterOperand(instruction);
184  }
185}
186
187void 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
201void InstructionSimplifierArm64Visitor::VisitUShr(HUShr* instruction) {
202  if (instruction->InputAt(1)->IsConstant()) {
203    TryMergeIntoUsersShifterOperand(instruction);
204  }
205}
206
207void InstructionSimplifierArm64Visitor::VisitXor(HXor* instruction) {
208  if (TryMergeNegatedInput(instruction)) {
209    RecordSimplification();
210  }
211}
212
213void InstructionSimplifierArm64Visitor::VisitVecMul(HVecMul* instruction) {
214  if (TryCombineVecMultiplyAccumulate(instruction, kArm64)) {
215    RecordSimplification();
216  }
217}
218
219}  // namespace arm64
220}  // namespace art
221