| Artem Udovichenko | 4a0dad6 | 2016-01-26 12:28:31 +0300 | [diff] [blame] | 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 |  | 
| Andreas Gampe | 8cf9cb3 | 2017-07-19 09:28:38 -0700 | [diff] [blame] | 17 | #include "instruction_simplifier_arm.h" | 
 | 18 |  | 
| Artem Serov | 328429f | 2016-07-06 16:23:04 +0100 | [diff] [blame] | 19 | #include "code_generator.h" | 
| Anton Kirilov | 74234da | 2017-01-13 14:42:47 +0000 | [diff] [blame] | 20 | #include "common_arm.h" | 
| Artem Udovichenko | 4a0dad6 | 2016-01-26 12:28:31 +0300 | [diff] [blame] | 21 | #include "instruction_simplifier_shared.h" | 
| Artem Serov | 328429f | 2016-07-06 16:23:04 +0100 | [diff] [blame] | 22 | #include "mirror/array-inl.h" | 
| Andreas Gampe | c6ea7d0 | 2017-02-01 16:46:28 -0800 | [diff] [blame] | 23 | #include "mirror/string.h" | 
| Anton Kirilov | 74234da | 2017-01-13 14:42:47 +0000 | [diff] [blame] | 24 | #include "nodes.h" | 
| Artem Udovichenko | 4a0dad6 | 2016-01-26 12:28:31 +0300 | [diff] [blame] | 25 |  | 
| Vladimir Marko | e272715 | 2019-10-10 10:46:42 +0100 | [diff] [blame^] | 26 | namespace art HIDDEN { | 
| Anton Kirilov | 74234da | 2017-01-13 14:42:47 +0000 | [diff] [blame] | 27 |  | 
 | 28 | using helpers::CanFitInShifterOperand; | 
 | 29 | using helpers::HasShifterOperand; | 
 | 30 |  | 
| Artem Udovichenko | 4a0dad6 | 2016-01-26 12:28:31 +0300 | [diff] [blame] | 31 | namespace arm { | 
 | 32 |  | 
| Vladimir Marko | 0f689e7 | 2017-10-02 12:38:21 +0100 | [diff] [blame] | 33 | class InstructionSimplifierArmVisitor : public HGraphVisitor { | 
 | 34 |  public: | 
 | 35 |   InstructionSimplifierArmVisitor(HGraph* graph, OptimizingCompilerStats* stats) | 
 | 36 |       : HGraphVisitor(graph), stats_(stats) {} | 
 | 37 |  | 
 | 38 |  private: | 
 | 39 |   void RecordSimplification() { | 
| Vladimir Marko | cd09e1f | 2017-11-24 15:02:40 +0000 | [diff] [blame] | 40 |     MaybeRecordStat(stats_, MethodCompilationStat::kInstructionSimplificationsArch); | 
| Vladimir Marko | 0f689e7 | 2017-10-02 12:38:21 +0100 | [diff] [blame] | 41 |   } | 
 | 42 |  | 
 | 43 |   bool TryMergeIntoUsersShifterOperand(HInstruction* instruction); | 
 | 44 |   bool TryMergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op, bool do_merge); | 
 | 45 |   bool CanMergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op) { | 
| Andreas Gampe | 3db7068 | 2018-12-26 15:12:03 -0800 | [diff] [blame] | 46 |     return TryMergeIntoShifterOperand(use, bitfield_op, /* do_merge= */ false); | 
| Vladimir Marko | 0f689e7 | 2017-10-02 12:38:21 +0100 | [diff] [blame] | 47 |   } | 
 | 48 |   bool MergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op) { | 
 | 49 |     DCHECK(CanMergeIntoShifterOperand(use, bitfield_op)); | 
| Andreas Gampe | 3db7068 | 2018-12-26 15:12:03 -0800 | [diff] [blame] | 50 |     return TryMergeIntoShifterOperand(use, bitfield_op, /* do_merge= */ true); | 
| Vladimir Marko | 0f689e7 | 2017-10-02 12:38:21 +0100 | [diff] [blame] | 51 |   } | 
 | 52 |  | 
 | 53 |   /** | 
 | 54 |    * This simplifier uses a special-purpose BB visitor. | 
 | 55 |    * (1) No need to visit Phi nodes. | 
 | 56 |    * (2) Since statements can be removed in a "forward" fashion, | 
 | 57 |    *     the visitor should test if each statement is still there. | 
 | 58 |    */ | 
| Roland Levillain | bbc6e7e | 2018-08-24 16:58:47 +0100 | [diff] [blame] | 59 |   void VisitBasicBlock(HBasicBlock* block) override { | 
| Vladimir Marko | 0f689e7 | 2017-10-02 12:38:21 +0100 | [diff] [blame] | 60 |     // TODO: fragile iteration, provide more robust iterators? | 
 | 61 |     for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { | 
 | 62 |       HInstruction* instruction = it.Current(); | 
 | 63 |       if (instruction->IsInBlock()) { | 
 | 64 |         instruction->Accept(this); | 
 | 65 |       } | 
 | 66 |     } | 
 | 67 |   } | 
 | 68 |  | 
| Roland Levillain | bbc6e7e | 2018-08-24 16:58:47 +0100 | [diff] [blame] | 69 |   void VisitAnd(HAnd* instruction) override; | 
 | 70 |   void VisitArrayGet(HArrayGet* instruction) override; | 
 | 71 |   void VisitArraySet(HArraySet* instruction) override; | 
 | 72 |   void VisitMul(HMul* instruction) override; | 
 | 73 |   void VisitOr(HOr* instruction) override; | 
 | 74 |   void VisitShl(HShl* instruction) override; | 
 | 75 |   void VisitShr(HShr* instruction) override; | 
 | 76 |   void VisitTypeConversion(HTypeConversion* instruction) override; | 
 | 77 |   void VisitUShr(HUShr* instruction) override; | 
| Vladimir Marko | 0f689e7 | 2017-10-02 12:38:21 +0100 | [diff] [blame] | 78 |  | 
 | 79 |   OptimizingCompilerStats* stats_; | 
 | 80 | }; | 
 | 81 |  | 
| Anton Kirilov | 74234da | 2017-01-13 14:42:47 +0000 | [diff] [blame] | 82 | bool InstructionSimplifierArmVisitor::TryMergeIntoShifterOperand(HInstruction* use, | 
 | 83 |                                                                  HInstruction* bitfield_op, | 
 | 84 |                                                                  bool do_merge) { | 
| Vladimir Marko | 33bff25 | 2017-11-01 14:35:42 +0000 | [diff] [blame] | 85 |   DCHECK(HasShifterOperand(use, InstructionSet::kArm)); | 
| Anton Kirilov | 74234da | 2017-01-13 14:42:47 +0000 | [diff] [blame] | 86 |   DCHECK(use->IsBinaryOperation()); | 
 | 87 |   DCHECK(CanFitInShifterOperand(bitfield_op)); | 
 | 88 |   DCHECK(!bitfield_op->HasEnvironmentUses()); | 
 | 89 |  | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 90 |   DataType::Type type = use->GetType(); | 
 | 91 |   if (type != DataType::Type::kInt32 && type != DataType::Type::kInt64) { | 
| Anton Kirilov | 74234da | 2017-01-13 14:42:47 +0000 | [diff] [blame] | 92 |     return false; | 
 | 93 |   } | 
 | 94 |  | 
 | 95 |   HInstruction* left = use->InputAt(0); | 
 | 96 |   HInstruction* right = use->InputAt(1); | 
 | 97 |   DCHECK(left == bitfield_op || right == bitfield_op); | 
 | 98 |  | 
 | 99 |   if (left == right) { | 
 | 100 |     // TODO: Handle special transformations in this situation? | 
 | 101 |     // For example should we transform `(x << 1) + (x << 1)` into `(x << 2)`? | 
 | 102 |     // Or should this be part of a separate transformation logic? | 
 | 103 |     return false; | 
 | 104 |   } | 
 | 105 |  | 
 | 106 |   bool is_commutative = use->AsBinaryOperation()->IsCommutative(); | 
 | 107 |   HInstruction* other_input; | 
 | 108 |   if (bitfield_op == right) { | 
 | 109 |     other_input = left; | 
 | 110 |   } else { | 
 | 111 |     if (is_commutative) { | 
 | 112 |       other_input = right; | 
 | 113 |     } else { | 
 | 114 |       return false; | 
 | 115 |     } | 
 | 116 |   } | 
 | 117 |  | 
 | 118 |   HDataProcWithShifterOp::OpKind op_kind; | 
 | 119 |   int shift_amount = 0; | 
 | 120 |  | 
 | 121 |   HDataProcWithShifterOp::GetOpInfoFromInstruction(bitfield_op, &op_kind, &shift_amount); | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 122 |   shift_amount &= use->GetType() == DataType::Type::kInt32 | 
| Anton Kirilov | 74234da | 2017-01-13 14:42:47 +0000 | [diff] [blame] | 123 |       ? kMaxIntShiftDistance | 
 | 124 |       : kMaxLongShiftDistance; | 
 | 125 |  | 
 | 126 |   if (HDataProcWithShifterOp::IsExtensionOp(op_kind)) { | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 127 |     if (!use->IsAdd() && (!use->IsSub() || use->GetType() != DataType::Type::kInt64)) { | 
| Anton Kirilov | 74234da | 2017-01-13 14:42:47 +0000 | [diff] [blame] | 128 |       return false; | 
 | 129 |     } | 
 | 130 |   // Shift by 1 is a special case that results in the same number and type of instructions | 
 | 131 |   // as this simplification, but potentially shorter code. | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 132 |   } else if (type == DataType::Type::kInt64 && shift_amount == 1) { | 
| Anton Kirilov | 74234da | 2017-01-13 14:42:47 +0000 | [diff] [blame] | 133 |     return false; | 
 | 134 |   } | 
 | 135 |  | 
 | 136 |   if (do_merge) { | 
 | 137 |     HDataProcWithShifterOp* alu_with_op = | 
| Vladimir Marko | ca6fff8 | 2017-10-03 14:49:14 +0100 | [diff] [blame] | 138 |         new (GetGraph()->GetAllocator()) HDataProcWithShifterOp(use, | 
 | 139 |                                                                 other_input, | 
 | 140 |                                                                 bitfield_op->InputAt(0), | 
 | 141 |                                                                 op_kind, | 
 | 142 |                                                                 shift_amount, | 
 | 143 |                                                                 use->GetDexPc()); | 
| Anton Kirilov | 74234da | 2017-01-13 14:42:47 +0000 | [diff] [blame] | 144 |     use->GetBlock()->ReplaceAndRemoveInstructionWith(use, alu_with_op); | 
 | 145 |     if (bitfield_op->GetUses().empty()) { | 
 | 146 |       bitfield_op->GetBlock()->RemoveInstruction(bitfield_op); | 
 | 147 |     } | 
| Artem Udovichenko | 4a0dad6 | 2016-01-26 12:28:31 +0300 | [diff] [blame] | 148 |     RecordSimplification(); | 
 | 149 |   } | 
| Anton Kirilov | 74234da | 2017-01-13 14:42:47 +0000 | [diff] [blame] | 150 |  | 
 | 151 |   return true; | 
| Artem Udovichenko | 4a0dad6 | 2016-01-26 12:28:31 +0300 | [diff] [blame] | 152 | } | 
 | 153 |  | 
| Anton Kirilov | 74234da | 2017-01-13 14:42:47 +0000 | [diff] [blame] | 154 | // Merge a bitfield move instruction into its uses if it can be merged in all of them. | 
 | 155 | bool InstructionSimplifierArmVisitor::TryMergeIntoUsersShifterOperand(HInstruction* bitfield_op) { | 
 | 156 |   DCHECK(CanFitInShifterOperand(bitfield_op)); | 
 | 157 |  | 
 | 158 |   if (bitfield_op->HasEnvironmentUses()) { | 
 | 159 |     return false; | 
| Artem Serov | 7fc6350 | 2016-02-09 17:15:29 +0000 | [diff] [blame] | 160 |   } | 
| Anton Kirilov | 74234da | 2017-01-13 14:42:47 +0000 | [diff] [blame] | 161 |  | 
 | 162 |   const HUseList<HInstruction*>& uses = bitfield_op->GetUses(); | 
 | 163 |  | 
 | 164 |   // Check whether we can merge the instruction in all its users' shifter operand. | 
 | 165 |   for (const HUseListNode<HInstruction*>& use : uses) { | 
 | 166 |     HInstruction* user = use.GetUser(); | 
| Vladimir Marko | 33bff25 | 2017-11-01 14:35:42 +0000 | [diff] [blame] | 167 |     if (!HasShifterOperand(user, InstructionSet::kArm)) { | 
| Anton Kirilov | 74234da | 2017-01-13 14:42:47 +0000 | [diff] [blame] | 168 |       return false; | 
 | 169 |     } | 
 | 170 |     if (!CanMergeIntoShifterOperand(user, bitfield_op)) { | 
 | 171 |       return false; | 
 | 172 |     } | 
 | 173 |   } | 
 | 174 |  | 
 | 175 |   // Merge the instruction into its uses. | 
 | 176 |   for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) { | 
 | 177 |     HInstruction* user = it->GetUser(); | 
 | 178 |     // Increment `it` now because `*it` will disappear thanks to MergeIntoShifterOperand(). | 
 | 179 |     ++it; | 
 | 180 |     bool merged = MergeIntoShifterOperand(user, bitfield_op); | 
 | 181 |     DCHECK(merged); | 
 | 182 |   } | 
 | 183 |  | 
 | 184 |   return true; | 
| Artem Serov | 7fc6350 | 2016-02-09 17:15:29 +0000 | [diff] [blame] | 185 | } | 
 | 186 |  | 
 | 187 | void InstructionSimplifierArmVisitor::VisitAnd(HAnd* instruction) { | 
 | 188 |   if (TryMergeNegatedInput(instruction)) { | 
 | 189 |     RecordSimplification(); | 
 | 190 |   } | 
 | 191 | } | 
 | 192 |  | 
| Artem Serov | 328429f | 2016-07-06 16:23:04 +0100 | [diff] [blame] | 193 | void InstructionSimplifierArmVisitor::VisitArrayGet(HArrayGet* instruction) { | 
 | 194 |   size_t data_offset = CodeGenerator::GetArrayDataOffset(instruction); | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 195 |   DataType::Type type = instruction->GetType(); | 
| Artem Serov | 328429f | 2016-07-06 16:23:04 +0100 | [diff] [blame] | 196 |  | 
| jessicahandojo | 0576575 | 2016-09-09 19:01:32 -0700 | [diff] [blame] | 197 |   // TODO: Implement reading (length + compression) for String compression feature from | 
| Roland Levillain | c043d00 | 2017-07-14 16:39:16 +0100 | [diff] [blame] | 198 |   // negative offset (count_offset - data_offset). Thumb2Assembler (now removed) did | 
 | 199 |   // not support T4 encoding of "LDR (immediate)", but ArmVIXLMacroAssembler might. | 
| jessicahandojo | 0576575 | 2016-09-09 19:01:32 -0700 | [diff] [blame] | 200 |   // Don't move array pointer if it is charAt because we need to take the count first. | 
 | 201 |   if (mirror::kUseStringCompression && instruction->IsStringCharAt()) { | 
 | 202 |     return; | 
 | 203 |   } | 
 | 204 |  | 
| Artem Serov | 0806f58 | 2018-10-11 20:14:20 +0100 | [diff] [blame] | 205 |   // TODO: Support intermediate address for object arrays on arm. | 
 | 206 |   if (type == DataType::Type::kReference) { | 
 | 207 |     return; | 
 | 208 |   } | 
 | 209 |  | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 210 |   if (type == DataType::Type::kInt64 | 
 | 211 |       || type == DataType::Type::kFloat32 | 
 | 212 |       || type == DataType::Type::kFloat64) { | 
| Artem Serov | 328429f | 2016-07-06 16:23:04 +0100 | [diff] [blame] | 213 |     // T32 doesn't support ShiftedRegOffset mem address mode for these types | 
 | 214 |     // to enable optimization. | 
 | 215 |     return; | 
 | 216 |   } | 
 | 217 |  | 
 | 218 |   if (TryExtractArrayAccessAddress(instruction, | 
 | 219 |                                    instruction->GetArray(), | 
 | 220 |                                    instruction->GetIndex(), | 
 | 221 |                                    data_offset)) { | 
 | 222 |     RecordSimplification(); | 
 | 223 |   } | 
 | 224 | } | 
 | 225 |  | 
 | 226 | void InstructionSimplifierArmVisitor::VisitArraySet(HArraySet* instruction) { | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 227 |   size_t access_size = DataType::Size(instruction->GetComponentType()); | 
| Artem Serov | 328429f | 2016-07-06 16:23:04 +0100 | [diff] [blame] | 228 |   size_t data_offset = mirror::Array::DataOffset(access_size).Uint32Value(); | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 229 |   DataType::Type type = instruction->GetComponentType(); | 
| Artem Serov | 328429f | 2016-07-06 16:23:04 +0100 | [diff] [blame] | 230 |  | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 231 |   if (type == DataType::Type::kInt64 | 
 | 232 |       || type == DataType::Type::kFloat32 | 
 | 233 |       || type == DataType::Type::kFloat64) { | 
| Artem Serov | 328429f | 2016-07-06 16:23:04 +0100 | [diff] [blame] | 234 |     // T32 doesn't support ShiftedRegOffset mem address mode for these types | 
 | 235 |     // to enable optimization. | 
 | 236 |     return; | 
 | 237 |   } | 
 | 238 |  | 
 | 239 |   if (TryExtractArrayAccessAddress(instruction, | 
 | 240 |                                    instruction->GetArray(), | 
 | 241 |                                    instruction->GetIndex(), | 
 | 242 |                                    data_offset)) { | 
 | 243 |     RecordSimplification(); | 
 | 244 |   } | 
 | 245 | } | 
| Artem Serov | 7fc6350 | 2016-02-09 17:15:29 +0000 | [diff] [blame] | 246 |  | 
| Anton Kirilov | 74234da | 2017-01-13 14:42:47 +0000 | [diff] [blame] | 247 | void InstructionSimplifierArmVisitor::VisitMul(HMul* instruction) { | 
| Vladimir Marko | 33bff25 | 2017-11-01 14:35:42 +0000 | [diff] [blame] | 248 |   if (TryCombineMultiplyAccumulate(instruction, InstructionSet::kArm)) { | 
| Anton Kirilov | 74234da | 2017-01-13 14:42:47 +0000 | [diff] [blame] | 249 |     RecordSimplification(); | 
 | 250 |   } | 
 | 251 | } | 
 | 252 |  | 
 | 253 | void InstructionSimplifierArmVisitor::VisitOr(HOr* instruction) { | 
 | 254 |   if (TryMergeNegatedInput(instruction)) { | 
 | 255 |     RecordSimplification(); | 
 | 256 |   } | 
 | 257 | } | 
 | 258 |  | 
 | 259 | void InstructionSimplifierArmVisitor::VisitShl(HShl* instruction) { | 
 | 260 |   if (instruction->InputAt(1)->IsConstant()) { | 
 | 261 |     TryMergeIntoUsersShifterOperand(instruction); | 
 | 262 |   } | 
 | 263 | } | 
 | 264 |  | 
 | 265 | void InstructionSimplifierArmVisitor::VisitShr(HShr* instruction) { | 
 | 266 |   if (instruction->InputAt(1)->IsConstant()) { | 
 | 267 |     TryMergeIntoUsersShifterOperand(instruction); | 
 | 268 |   } | 
 | 269 | } | 
 | 270 |  | 
 | 271 | void InstructionSimplifierArmVisitor::VisitTypeConversion(HTypeConversion* instruction) { | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 272 |   DataType::Type result_type = instruction->GetResultType(); | 
 | 273 |   DataType::Type input_type = instruction->GetInputType(); | 
| Anton Kirilov | 74234da | 2017-01-13 14:42:47 +0000 | [diff] [blame] | 274 |  | 
 | 275 |   if (input_type == result_type) { | 
 | 276 |     // We let the arch-independent code handle this. | 
 | 277 |     return; | 
 | 278 |   } | 
 | 279 |  | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 280 |   if (DataType::IsIntegralType(result_type) && DataType::IsIntegralType(input_type)) { | 
| Anton Kirilov | 74234da | 2017-01-13 14:42:47 +0000 | [diff] [blame] | 281 |     TryMergeIntoUsersShifterOperand(instruction); | 
 | 282 |   } | 
 | 283 | } | 
 | 284 |  | 
 | 285 | void InstructionSimplifierArmVisitor::VisitUShr(HUShr* instruction) { | 
 | 286 |   if (instruction->InputAt(1)->IsConstant()) { | 
 | 287 |     TryMergeIntoUsersShifterOperand(instruction); | 
 | 288 |   } | 
 | 289 | } | 
 | 290 |  | 
| Aart Bik | 2477320 | 2018-04-26 10:28:51 -0700 | [diff] [blame] | 291 | bool InstructionSimplifierArm::Run() { | 
| Vladimir Marko | 0f689e7 | 2017-10-02 12:38:21 +0100 | [diff] [blame] | 292 |   InstructionSimplifierArmVisitor visitor(graph_, stats_); | 
 | 293 |   visitor.VisitReversePostOrder(); | 
| Aart Bik | 2477320 | 2018-04-26 10:28:51 -0700 | [diff] [blame] | 294 |   return true; | 
| Vladimir Marko | 0f689e7 | 2017-10-02 12:38:21 +0100 | [diff] [blame] | 295 | } | 
 | 296 |  | 
| Artem Udovichenko | 4a0dad6 | 2016-01-26 12:28:31 +0300 | [diff] [blame] | 297 | }  // namespace arm | 
 | 298 | }  // namespace art |