| Nicolas Geoffray | 3c04974 | 2014-09-24 18:10:46 +0100 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright (C) 2014 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.h" | 
 | 18 |  | 
| Andreas Gampe | c6ea7d0 | 2017-02-01 16:46:28 -0800 | [diff] [blame] | 19 | #include "art_method-inl.h" | 
 | 20 | #include "class_linker-inl.h" | 
| Vladimir Marko | 5868ada | 2020-05-12 11:50:34 +0100 | [diff] [blame] | 21 | #include "class_root-inl.h" | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 22 | #include "data_type-inl.h" | 
| Aart Bik | 71bf7b4 | 2016-11-16 10:17:46 -0800 | [diff] [blame] | 23 | #include "escape.h" | 
| Nicolas Geoffray | a83a54d | 2015-10-02 17:30:26 +0100 | [diff] [blame] | 24 | #include "intrinsics.h" | 
| Calin Juravle | acf735c | 2015-02-12 15:25:22 +0000 | [diff] [blame] | 25 | #include "mirror/class-inl.h" | 
| Mathieu Chartier | 0795f23 | 2016-09-27 18:43:30 -0700 | [diff] [blame] | 26 | #include "scoped_thread_state_change-inl.h" | 
| Andreas Gampe | 8cf9cb3 | 2017-07-19 09:28:38 -0700 | [diff] [blame] | 27 | #include "sharpening.h" | 
| Vladimir Marko | 552a134 | 2017-10-31 10:56:47 +0000 | [diff] [blame] | 28 | #include "string_builder_append.h" | 
| Calin Juravle | acf735c | 2015-02-12 15:25:22 +0000 | [diff] [blame] | 29 |  | 
| Vladimir Marko | 0a51605 | 2019-10-14 13:00:44 +0000 | [diff] [blame] | 30 | namespace art { | 
| Nicolas Geoffray | 3c04974 | 2014-09-24 18:10:46 +0100 | [diff] [blame] | 31 |  | 
| Artem Serov | cced8ba | 2017-07-19 18:18:09 +0100 | [diff] [blame] | 32 | // Whether to run an exhaustive test of individual HInstructions cloning when each instruction | 
 | 33 | // is replaced with its copy if it is clonable. | 
 | 34 | static constexpr bool kTestInstructionClonerExhaustively = false; | 
 | 35 |  | 
| Nicolas Geoffray | a83a54d | 2015-10-02 17:30:26 +0100 | [diff] [blame] | 36 | class InstructionSimplifierVisitor : public HGraphDelegateVisitor { | 
| Nicolas Geoffray | 5e6916c | 2014-11-18 16:53:35 +0000 | [diff] [blame] | 37 |  public: | 
| Nicolas Geoffray | c4aa82c | 2017-03-06 14:38:52 +0000 | [diff] [blame] | 38 |   InstructionSimplifierVisitor(HGraph* graph, | 
 | 39 |                                CodeGenerator* codegen, | 
| Evgeny Astigeevich | 6587d91 | 2020-06-12 10:51:43 +0100 | [diff] [blame] | 40 |                                OptimizingCompilerStats* stats, | 
 | 41 |                                bool be_loop_friendly) | 
| Nicolas Geoffray | a83a54d | 2015-10-02 17:30:26 +0100 | [diff] [blame] | 42 |       : HGraphDelegateVisitor(graph), | 
| Nicolas Geoffray | c4aa82c | 2017-03-06 14:38:52 +0000 | [diff] [blame] | 43 |         codegen_(codegen), | 
| Evgeny Astigeevich | 6587d91 | 2020-06-12 10:51:43 +0100 | [diff] [blame] | 44 |         stats_(stats), | 
 | 45 |         be_loop_friendly_(be_loop_friendly) {} | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 46 |  | 
| Aart Bik | 2477320 | 2018-04-26 10:28:51 -0700 | [diff] [blame] | 47 |   bool Run(); | 
| Nicolas Geoffray | 5e6916c | 2014-11-18 16:53:35 +0000 | [diff] [blame] | 48 |  | 
 | 49 |  private: | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 50 |   void RecordSimplification() { | 
 | 51 |     simplification_occurred_ = true; | 
 | 52 |     simplifications_at_current_position_++; | 
| Vladimir Marko | cd09e1f | 2017-11-24 15:02:40 +0000 | [diff] [blame] | 53 |     MaybeRecordStat(stats_, MethodCompilationStat::kInstructionSimplifications); | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 54 |   } | 
 | 55 |  | 
| Scott Wakeling | 40a04bf | 2015-12-11 09:50:36 +0000 | [diff] [blame] | 56 |   bool ReplaceRotateWithRor(HBinaryOperation* op, HUShr* ushr, HShl* shl); | 
 | 57 |   bool TryReplaceWithRotate(HBinaryOperation* instruction); | 
 | 58 |   bool TryReplaceWithRotateConstantPattern(HBinaryOperation* op, HUShr* ushr, HShl* shl); | 
 | 59 |   bool TryReplaceWithRotateRegisterNegPattern(HBinaryOperation* op, HUShr* ushr, HShl* shl); | 
 | 60 |   bool TryReplaceWithRotateRegisterSubPattern(HBinaryOperation* op, HUShr* ushr, HShl* shl); | 
 | 61 |  | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 62 |   bool TryMoveNegOnInputsAfterBinop(HBinaryOperation* binop); | 
| Alexandre Rames | ca0e3a0 | 2016-02-03 10:54:07 +0000 | [diff] [blame] | 63 |   // `op` should be either HOr or HAnd. | 
 | 64 |   // De Morgan's laws: | 
 | 65 |   // ~a & ~b = ~(a | b)  and  ~a | ~b = ~(a & b) | 
 | 66 |   bool TryDeMorganNegationFactoring(HBinaryOperation* op); | 
| Anton Kirilov | e14dc86 | 2016-05-13 17:56:15 +0100 | [diff] [blame] | 67 |   bool TryHandleAssociativeAndCommutativeOperation(HBinaryOperation* instruction); | 
 | 68 |   bool TrySubtractionChainSimplification(HBinaryOperation* instruction); | 
| Lena Djokic | bc5460b | 2017-07-20 16:07:36 +0200 | [diff] [blame] | 69 |   bool TryCombineVecMultiplyAccumulate(HVecMul* mul); | 
| Evgeny Astigeevich | 6587d91 | 2020-06-12 10:51:43 +0100 | [diff] [blame] | 70 |   void TryToReuseDiv(HRem* rem); | 
| Anton Kirilov | e14dc86 | 2016-05-13 17:56:15 +0100 | [diff] [blame] | 71 |  | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 72 |   void VisitShift(HBinaryOperation* shift); | 
| Roland Levillain | bbc6e7e | 2018-08-24 16:58:47 +0100 | [diff] [blame] | 73 |   void VisitEqual(HEqual* equal) override; | 
 | 74 |   void VisitNotEqual(HNotEqual* equal) override; | 
 | 75 |   void VisitBooleanNot(HBooleanNot* bool_not) override; | 
 | 76 |   void VisitInstanceFieldSet(HInstanceFieldSet* equal) override; | 
 | 77 |   void VisitStaticFieldSet(HStaticFieldSet* equal) override; | 
 | 78 |   void VisitArraySet(HArraySet* equal) override; | 
 | 79 |   void VisitTypeConversion(HTypeConversion* instruction) override; | 
 | 80 |   void VisitNullCheck(HNullCheck* instruction) override; | 
 | 81 |   void VisitArrayLength(HArrayLength* instruction) override; | 
 | 82 |   void VisitCheckCast(HCheckCast* instruction) override; | 
 | 83 |   void VisitAbs(HAbs* instruction) override; | 
 | 84 |   void VisitAdd(HAdd* instruction) override; | 
 | 85 |   void VisitAnd(HAnd* instruction) override; | 
 | 86 |   void VisitCondition(HCondition* instruction) override; | 
 | 87 |   void VisitGreaterThan(HGreaterThan* condition) override; | 
 | 88 |   void VisitGreaterThanOrEqual(HGreaterThanOrEqual* condition) override; | 
 | 89 |   void VisitLessThan(HLessThan* condition) override; | 
 | 90 |   void VisitLessThanOrEqual(HLessThanOrEqual* condition) override; | 
 | 91 |   void VisitBelow(HBelow* condition) override; | 
 | 92 |   void VisitBelowOrEqual(HBelowOrEqual* condition) override; | 
 | 93 |   void VisitAbove(HAbove* condition) override; | 
 | 94 |   void VisitAboveOrEqual(HAboveOrEqual* condition) override; | 
 | 95 |   void VisitDiv(HDiv* instruction) override; | 
| Evgeny Astigeevich | 6587d91 | 2020-06-12 10:51:43 +0100 | [diff] [blame] | 96 |   void VisitRem(HRem* instruction) override; | 
| Roland Levillain | bbc6e7e | 2018-08-24 16:58:47 +0100 | [diff] [blame] | 97 |   void VisitMul(HMul* instruction) override; | 
 | 98 |   void VisitNeg(HNeg* instruction) override; | 
 | 99 |   void VisitNot(HNot* instruction) override; | 
 | 100 |   void VisitOr(HOr* instruction) override; | 
 | 101 |   void VisitShl(HShl* instruction) override; | 
 | 102 |   void VisitShr(HShr* instruction) override; | 
 | 103 |   void VisitSub(HSub* instruction) override; | 
 | 104 |   void VisitUShr(HUShr* instruction) override; | 
 | 105 |   void VisitXor(HXor* instruction) override; | 
 | 106 |   void VisitSelect(HSelect* select) override; | 
 | 107 |   void VisitIf(HIf* instruction) override; | 
 | 108 |   void VisitInstanceOf(HInstanceOf* instruction) override; | 
 | 109 |   void VisitInvoke(HInvoke* invoke) override; | 
 | 110 |   void VisitDeoptimize(HDeoptimize* deoptimize) override; | 
 | 111 |   void VisitVecMul(HVecMul* instruction) override; | 
| Nicolas Geoffray | 6e7455e | 2015-09-28 16:25:37 +0100 | [diff] [blame] | 112 |  | 
 | 113 |   bool CanEnsureNotNullAt(HInstruction* instr, HInstruction* at) const; | 
| Calin Juravle | acf735c | 2015-02-12 15:25:22 +0000 | [diff] [blame] | 114 |  | 
| Nicolas Geoffray | ee3cf07 | 2015-10-06 11:45:02 +0100 | [diff] [blame] | 115 |   void SimplifySystemArrayCopy(HInvoke* invoke); | 
 | 116 |   void SimplifyStringEquals(HInvoke* invoke); | 
| Aart Bik | 2a6aad9 | 2016-02-25 11:32:32 -0800 | [diff] [blame] | 117 |   void SimplifyFP2Int(HInvoke* invoke); | 
| Vladimir Marko | 87f3fcb | 2016-04-28 15:52:11 +0100 | [diff] [blame] | 118 |   void SimplifyStringCharAt(HInvoke* invoke); | 
| Vladimir Marko | 5f84607 | 2020-04-09 13:20:11 +0100 | [diff] [blame] | 119 |   void SimplifyStringLength(HInvoke* invoke); | 
| Vladimir Marko | 6fa4404 | 2018-03-19 18:42:49 +0000 | [diff] [blame] | 120 |   void SimplifyStringIndexOf(HInvoke* invoke); | 
| Aart Bik | ff7d89c | 2016-11-07 08:49:28 -0800 | [diff] [blame] | 121 |   void SimplifyNPEOnArgN(HInvoke* invoke, size_t); | 
| Aart Bik | 71bf7b4 | 2016-11-16 10:17:46 -0800 | [diff] [blame] | 122 |   void SimplifyReturnThis(HInvoke* invoke); | 
 | 123 |   void SimplifyAllocationIntrinsic(HInvoke* invoke); | 
| Nicolas Geoffray | ee3cf07 | 2015-10-06 11:45:02 +0100 | [diff] [blame] | 124 |  | 
| Nicolas Geoffray | c4aa82c | 2017-03-06 14:38:52 +0000 | [diff] [blame] | 125 |   CodeGenerator* codegen_; | 
| Calin Juravle | acf735c | 2015-02-12 15:25:22 +0000 | [diff] [blame] | 126 |   OptimizingCompilerStats* stats_; | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 127 |   bool simplification_occurred_ = false; | 
 | 128 |   int simplifications_at_current_position_ = 0; | 
| Evgeny Astigeevich | 6587d91 | 2020-06-12 10:51:43 +0100 | [diff] [blame] | 129 |   // Prohibit optimizations which can affect HInductionVarAnalysis/HLoopOptimization | 
 | 130 |   // and prevent loop optimizations: | 
 | 131 |   //   true - avoid such optimizations. | 
 | 132 |   //   false - allow such optimizations. | 
 | 133 |   // Checked by the following optimizations: | 
 | 134 |   //   - TryToReuseDiv: simplification of Div+Rem into Div+Mul+Sub. | 
 | 135 |   bool be_loop_friendly_; | 
| Aart Bik | 2767f4b | 2016-10-28 15:03:53 -0700 | [diff] [blame] | 136 |   // We ensure we do not loop infinitely. The value should not be too high, since that | 
 | 137 |   // would allow looping around the same basic block too many times. The value should | 
 | 138 |   // not be too low either, however, since we want to allow revisiting a basic block | 
 | 139 |   // with many statements and simplifications at least once. | 
 | 140 |   static constexpr int kMaxSamePositionSimplifications = 50; | 
| Nicolas Geoffray | 5e6916c | 2014-11-18 16:53:35 +0000 | [diff] [blame] | 141 | }; | 
 | 142 |  | 
| Aart Bik | 2477320 | 2018-04-26 10:28:51 -0700 | [diff] [blame] | 143 | bool InstructionSimplifier::Run() { | 
| Artem Serov | cced8ba | 2017-07-19 18:18:09 +0100 | [diff] [blame] | 144 |   if (kTestInstructionClonerExhaustively) { | 
 | 145 |     CloneAndReplaceInstructionVisitor visitor(graph_); | 
 | 146 |     visitor.VisitReversePostOrder(); | 
 | 147 |   } | 
 | 148 |  | 
| Evgeny Astigeevich | 6587d91 | 2020-06-12 10:51:43 +0100 | [diff] [blame] | 149 |   bool be_loop_friendly = (use_all_optimizations_ == false); | 
 | 150 |  | 
 | 151 |   InstructionSimplifierVisitor visitor(graph_, codegen_, stats_, be_loop_friendly); | 
| Aart Bik | 2477320 | 2018-04-26 10:28:51 -0700 | [diff] [blame] | 152 |   return visitor.Run(); | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 153 | } | 
 | 154 |  | 
| Aart Bik | 2477320 | 2018-04-26 10:28:51 -0700 | [diff] [blame] | 155 | bool InstructionSimplifierVisitor::Run() { | 
 | 156 |   bool didSimplify = false; | 
| Nicolas Geoffray | 07276db | 2015-05-18 14:22:09 +0100 | [diff] [blame] | 157 |   // Iterate in reverse post order to open up more simplifications to users | 
 | 158 |   // of instructions that got simplified. | 
| Vladimir Marko | 2c45bc9 | 2016-10-25 16:54:12 +0100 | [diff] [blame] | 159 |   for (HBasicBlock* block : GetGraph()->GetReversePostOrder()) { | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 160 |     // The simplification of an instruction to another instruction may yield | 
 | 161 |     // possibilities for other simplifications. So although we perform a reverse | 
 | 162 |     // post order visit, we sometimes need to revisit an instruction index. | 
| Vladimir Marko | 2c45bc9 | 2016-10-25 16:54:12 +0100 | [diff] [blame] | 163 |     do { | 
 | 164 |       simplification_occurred_ = false; | 
 | 165 |       VisitBasicBlock(block); | 
| Aart Bik | 2477320 | 2018-04-26 10:28:51 -0700 | [diff] [blame] | 166 |       if (simplification_occurred_) { | 
 | 167 |         didSimplify = true; | 
 | 168 |       } | 
| Vladimir Marko | 2c45bc9 | 2016-10-25 16:54:12 +0100 | [diff] [blame] | 169 |     } while (simplification_occurred_ && | 
 | 170 |              (simplifications_at_current_position_ < kMaxSamePositionSimplifications)); | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 171 |     simplifications_at_current_position_ = 0; | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 172 |   } | 
| Aart Bik | 2477320 | 2018-04-26 10:28:51 -0700 | [diff] [blame] | 173 |   return didSimplify; | 
| Nicolas Geoffray | 3c04974 | 2014-09-24 18:10:46 +0100 | [diff] [blame] | 174 | } | 
 | 175 |  | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 176 | namespace { | 
 | 177 |  | 
 | 178 | bool AreAllBitsSet(HConstant* constant) { | 
 | 179 |   return Int64FromConstant(constant) == -1; | 
 | 180 | } | 
 | 181 |  | 
 | 182 | }  // namespace | 
 | 183 |  | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 184 | // Returns true if the code was simplified to use only one negation operation | 
 | 185 | // after the binary operation instead of one on each of the inputs. | 
 | 186 | bool InstructionSimplifierVisitor::TryMoveNegOnInputsAfterBinop(HBinaryOperation* binop) { | 
 | 187 |   DCHECK(binop->IsAdd() || binop->IsSub()); | 
 | 188 |   DCHECK(binop->GetLeft()->IsNeg() && binop->GetRight()->IsNeg()); | 
 | 189 |   HNeg* left_neg = binop->GetLeft()->AsNeg(); | 
 | 190 |   HNeg* right_neg = binop->GetRight()->AsNeg(); | 
 | 191 |   if (!left_neg->HasOnlyOneNonEnvironmentUse() || | 
 | 192 |       !right_neg->HasOnlyOneNonEnvironmentUse()) { | 
 | 193 |     return false; | 
 | 194 |   } | 
 | 195 |   // Replace code looking like | 
 | 196 |   //    NEG tmp1, a | 
 | 197 |   //    NEG tmp2, b | 
 | 198 |   //    ADD dst, tmp1, tmp2 | 
 | 199 |   // with | 
 | 200 |   //    ADD tmp, a, b | 
 | 201 |   //    NEG dst, tmp | 
| Serdjuk, Nikolay Y | aae9e66 | 2015-08-21 13:26:34 +0600 | [diff] [blame] | 202 |   // Note that we cannot optimize `(-a) + (-b)` to `-(a + b)` for floating-point. | 
 | 203 |   // When `a` is `-0.0` and `b` is `0.0`, the former expression yields `0.0`, | 
 | 204 |   // while the later yields `-0.0`. | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 205 |   if (!DataType::IsIntegralType(binop->GetType())) { | 
| Serdjuk, Nikolay Y | aae9e66 | 2015-08-21 13:26:34 +0600 | [diff] [blame] | 206 |     return false; | 
 | 207 |   } | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 208 |   binop->ReplaceInput(left_neg->GetInput(), 0); | 
 | 209 |   binop->ReplaceInput(right_neg->GetInput(), 1); | 
 | 210 |   left_neg->GetBlock()->RemoveInstruction(left_neg); | 
 | 211 |   right_neg->GetBlock()->RemoveInstruction(right_neg); | 
| Vladimir Marko | ca6fff8 | 2017-10-03 14:49:14 +0100 | [diff] [blame] | 212 |   HNeg* neg = new (GetGraph()->GetAllocator()) HNeg(binop->GetType(), binop); | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 213 |   binop->GetBlock()->InsertInstructionBefore(neg, binop->GetNext()); | 
 | 214 |   binop->ReplaceWithExceptInReplacementAtIndex(neg, 0); | 
 | 215 |   RecordSimplification(); | 
 | 216 |   return true; | 
 | 217 | } | 
 | 218 |  | 
| Alexandre Rames | ca0e3a0 | 2016-02-03 10:54:07 +0000 | [diff] [blame] | 219 | bool InstructionSimplifierVisitor::TryDeMorganNegationFactoring(HBinaryOperation* op) { | 
 | 220 |   DCHECK(op->IsAnd() || op->IsOr()) << op->DebugName(); | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 221 |   DataType::Type type = op->GetType(); | 
| Alexandre Rames | ca0e3a0 | 2016-02-03 10:54:07 +0000 | [diff] [blame] | 222 |   HInstruction* left = op->GetLeft(); | 
 | 223 |   HInstruction* right = op->GetRight(); | 
 | 224 |  | 
 | 225 |   // We can apply De Morgan's laws if both inputs are Not's and are only used | 
 | 226 |   // by `op`. | 
| Alexandre Rames | 9f98025 | 2016-02-05 14:00:28 +0000 | [diff] [blame] | 227 |   if (((left->IsNot() && right->IsNot()) || | 
 | 228 |        (left->IsBooleanNot() && right->IsBooleanNot())) && | 
| Alexandre Rames | ca0e3a0 | 2016-02-03 10:54:07 +0000 | [diff] [blame] | 229 |       left->HasOnlyOneNonEnvironmentUse() && | 
 | 230 |       right->HasOnlyOneNonEnvironmentUse()) { | 
 | 231 |     // Replace code looking like | 
 | 232 |     //    NOT nota, a | 
 | 233 |     //    NOT notb, b | 
 | 234 |     //    AND dst, nota, notb (respectively OR) | 
 | 235 |     // with | 
 | 236 |     //    OR or, a, b         (respectively AND) | 
 | 237 |     //    NOT dest, or | 
| Alexandre Rames | 9f98025 | 2016-02-05 14:00:28 +0000 | [diff] [blame] | 238 |     HInstruction* src_left = left->InputAt(0); | 
 | 239 |     HInstruction* src_right = right->InputAt(0); | 
| Alexandre Rames | ca0e3a0 | 2016-02-03 10:54:07 +0000 | [diff] [blame] | 240 |     uint32_t dex_pc = op->GetDexPc(); | 
 | 241 |  | 
 | 242 |     // Remove the negations on the inputs. | 
 | 243 |     left->ReplaceWith(src_left); | 
 | 244 |     right->ReplaceWith(src_right); | 
 | 245 |     left->GetBlock()->RemoveInstruction(left); | 
 | 246 |     right->GetBlock()->RemoveInstruction(right); | 
 | 247 |  | 
 | 248 |     // Replace the `HAnd` or `HOr`. | 
 | 249 |     HBinaryOperation* hbin; | 
 | 250 |     if (op->IsAnd()) { | 
| Vladimir Marko | ca6fff8 | 2017-10-03 14:49:14 +0100 | [diff] [blame] | 251 |       hbin = new (GetGraph()->GetAllocator()) HOr(type, src_left, src_right, dex_pc); | 
| Alexandre Rames | ca0e3a0 | 2016-02-03 10:54:07 +0000 | [diff] [blame] | 252 |     } else { | 
| Vladimir Marko | ca6fff8 | 2017-10-03 14:49:14 +0100 | [diff] [blame] | 253 |       hbin = new (GetGraph()->GetAllocator()) HAnd(type, src_left, src_right, dex_pc); | 
| Alexandre Rames | ca0e3a0 | 2016-02-03 10:54:07 +0000 | [diff] [blame] | 254 |     } | 
| Alexandre Rames | 9f98025 | 2016-02-05 14:00:28 +0000 | [diff] [blame] | 255 |     HInstruction* hnot; | 
 | 256 |     if (left->IsBooleanNot()) { | 
| Vladimir Marko | ca6fff8 | 2017-10-03 14:49:14 +0100 | [diff] [blame] | 257 |       hnot = new (GetGraph()->GetAllocator()) HBooleanNot(hbin, dex_pc); | 
| Alexandre Rames | 9f98025 | 2016-02-05 14:00:28 +0000 | [diff] [blame] | 258 |     } else { | 
| Vladimir Marko | ca6fff8 | 2017-10-03 14:49:14 +0100 | [diff] [blame] | 259 |       hnot = new (GetGraph()->GetAllocator()) HNot(type, hbin, dex_pc); | 
| Alexandre Rames | 9f98025 | 2016-02-05 14:00:28 +0000 | [diff] [blame] | 260 |     } | 
| Alexandre Rames | ca0e3a0 | 2016-02-03 10:54:07 +0000 | [diff] [blame] | 261 |  | 
 | 262 |     op->GetBlock()->InsertInstructionBefore(hbin, op); | 
 | 263 |     op->GetBlock()->ReplaceAndRemoveInstructionWith(op, hnot); | 
 | 264 |  | 
 | 265 |     RecordSimplification(); | 
 | 266 |     return true; | 
 | 267 |   } | 
 | 268 |  | 
 | 269 |   return false; | 
 | 270 | } | 
 | 271 |  | 
| Lena Djokic | bc5460b | 2017-07-20 16:07:36 +0200 | [diff] [blame] | 272 | bool InstructionSimplifierVisitor::TryCombineVecMultiplyAccumulate(HVecMul* mul) { | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 273 |   DataType::Type type = mul->GetPackedType(); | 
| Lena Djokic | bc5460b | 2017-07-20 16:07:36 +0200 | [diff] [blame] | 274 |   InstructionSet isa = codegen_->GetInstructionSet(); | 
 | 275 |   switch (isa) { | 
| Vladimir Marko | 33bff25 | 2017-11-01 14:35:42 +0000 | [diff] [blame] | 276 |     case InstructionSet::kArm64: | 
| Vladimir Marko | d5d2f2c | 2017-09-26 12:37:26 +0100 | [diff] [blame] | 277 |       if (!(type == DataType::Type::kUint8 || | 
 | 278 |             type == DataType::Type::kInt8 || | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 279 |             type == DataType::Type::kUint16 || | 
 | 280 |             type == DataType::Type::kInt16 || | 
 | 281 |             type == DataType::Type::kInt32)) { | 
| Lena Djokic | bc5460b | 2017-07-20 16:07:36 +0200 | [diff] [blame] | 282 |         return false; | 
 | 283 |       } | 
 | 284 |       break; | 
| Lena Djokic | bc5460b | 2017-07-20 16:07:36 +0200 | [diff] [blame] | 285 |     default: | 
 | 286 |       return false; | 
 | 287 |   } | 
 | 288 |  | 
| Vladimir Marko | e764d2e | 2017-10-05 14:35:55 +0100 | [diff] [blame] | 289 |   ArenaAllocator* allocator = mul->GetBlock()->GetGraph()->GetAllocator(); | 
| Lena Djokic | bc5460b | 2017-07-20 16:07:36 +0200 | [diff] [blame] | 290 |  | 
 | 291 |   if (mul->HasOnlyOneNonEnvironmentUse()) { | 
 | 292 |     HInstruction* use = mul->GetUses().front().GetUser(); | 
 | 293 |     if (use->IsVecAdd() || use->IsVecSub()) { | 
 | 294 |       // Replace code looking like | 
 | 295 |       //    VECMUL tmp, x, y | 
 | 296 |       //    VECADD/SUB dst, acc, tmp | 
 | 297 |       // with | 
 | 298 |       //    VECMULACC dst, acc, x, y | 
 | 299 |       // Note that we do not want to (unconditionally) perform the merge when the | 
 | 300 |       // multiplication has multiple uses and it can be merged in all of them. | 
 | 301 |       // Multiple uses could happen on the same control-flow path, and we would | 
 | 302 |       // then increase the amount of work. In the future we could try to evaluate | 
 | 303 |       // whether all uses are on different control-flow paths (using dominance and | 
 | 304 |       // reverse-dominance information) and only perform the merge when they are. | 
 | 305 |       HInstruction* accumulator = nullptr; | 
 | 306 |       HVecBinaryOperation* binop = use->AsVecBinaryOperation(); | 
 | 307 |       HInstruction* binop_left = binop->GetLeft(); | 
 | 308 |       HInstruction* binop_right = binop->GetRight(); | 
 | 309 |       // This is always true since the `HVecMul` has only one use (which is checked above). | 
 | 310 |       DCHECK_NE(binop_left, binop_right); | 
 | 311 |       if (binop_right == mul) { | 
 | 312 |         accumulator = binop_left; | 
 | 313 |       } else if (use->IsVecAdd()) { | 
 | 314 |         DCHECK_EQ(binop_left, mul); | 
 | 315 |         accumulator = binop_right; | 
 | 316 |       } | 
 | 317 |  | 
 | 318 |       HInstruction::InstructionKind kind = | 
 | 319 |           use->IsVecAdd() ? HInstruction::kAdd : HInstruction::kSub; | 
 | 320 |       if (accumulator != nullptr) { | 
 | 321 |         HVecMultiplyAccumulate* mulacc = | 
| Vladimir Marko | e764d2e | 2017-10-05 14:35:55 +0100 | [diff] [blame] | 322 |             new (allocator) HVecMultiplyAccumulate(allocator, | 
 | 323 |                                                    kind, | 
 | 324 |                                                    accumulator, | 
 | 325 |                                                    mul->GetLeft(), | 
 | 326 |                                                    mul->GetRight(), | 
 | 327 |                                                    binop->GetPackedType(), | 
 | 328 |                                                    binop->GetVectorLength(), | 
 | 329 |                                                    binop->GetDexPc()); | 
| Lena Djokic | bc5460b | 2017-07-20 16:07:36 +0200 | [diff] [blame] | 330 |  | 
 | 331 |         binop->GetBlock()->ReplaceAndRemoveInstructionWith(binop, mulacc); | 
 | 332 |         DCHECK(!mul->HasUses()); | 
 | 333 |         mul->GetBlock()->RemoveInstruction(mul); | 
 | 334 |         return true; | 
 | 335 |       } | 
 | 336 |     } | 
 | 337 |   } | 
 | 338 |  | 
 | 339 |   return false; | 
 | 340 | } | 
 | 341 |  | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 342 | void InstructionSimplifierVisitor::VisitShift(HBinaryOperation* instruction) { | 
 | 343 |   DCHECK(instruction->IsShl() || instruction->IsShr() || instruction->IsUShr()); | 
| Alexandre Rames | 5051844 | 2016-06-27 11:39:19 +0100 | [diff] [blame] | 344 |   HInstruction* shift_amount = instruction->GetRight(); | 
 | 345 |   HInstruction* value = instruction->GetLeft(); | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 346 |  | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 347 |   int64_t implicit_mask = (value->GetType() == DataType::Type::kInt64) | 
| Alexandre Rames | 5051844 | 2016-06-27 11:39:19 +0100 | [diff] [blame] | 348 |       ? kMaxLongShiftDistance | 
 | 349 |       : kMaxIntShiftDistance; | 
 | 350 |  | 
 | 351 |   if (shift_amount->IsConstant()) { | 
 | 352 |     int64_t cst = Int64FromConstant(shift_amount->AsConstant()); | 
| Aart Bik | 50e20d5 | 2017-05-05 14:07:29 -0700 | [diff] [blame] | 353 |     int64_t masked_cst = cst & implicit_mask; | 
 | 354 |     if (masked_cst == 0) { | 
| Mark Mendell | ba56d06 | 2015-05-05 21:34:03 -0400 | [diff] [blame] | 355 |       // Replace code looking like | 
| Alexandre Rames | 5051844 | 2016-06-27 11:39:19 +0100 | [diff] [blame] | 356 |       //    SHL dst, value, 0 | 
| Mark Mendell | ba56d06 | 2015-05-05 21:34:03 -0400 | [diff] [blame] | 357 |       // with | 
| Alexandre Rames | 5051844 | 2016-06-27 11:39:19 +0100 | [diff] [blame] | 358 |       //    value | 
 | 359 |       instruction->ReplaceWith(value); | 
| Mark Mendell | ba56d06 | 2015-05-05 21:34:03 -0400 | [diff] [blame] | 360 |       instruction->GetBlock()->RemoveInstruction(instruction); | 
| Alexandre Rames | c5809c3 | 2016-05-25 15:01:06 +0100 | [diff] [blame] | 361 |       RecordSimplification(); | 
| Alexandre Rames | 5051844 | 2016-06-27 11:39:19 +0100 | [diff] [blame] | 362 |       return; | 
| Aart Bik | 50e20d5 | 2017-05-05 14:07:29 -0700 | [diff] [blame] | 363 |     } else if (masked_cst != cst) { | 
 | 364 |       // Replace code looking like | 
 | 365 |       //    SHL dst, value, cst | 
 | 366 |       // where cst exceeds maximum distance with the equivalent | 
 | 367 |       //    SHL dst, value, cst & implicit_mask | 
 | 368 |       // (as defined by shift semantics). This ensures other | 
 | 369 |       // optimizations do not need to special case for such situations. | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 370 |       DCHECK_EQ(shift_amount->GetType(), DataType::Type::kInt32); | 
| Andreas Gampe | 3db7068 | 2018-12-26 15:12:03 -0800 | [diff] [blame] | 371 |       instruction->ReplaceInput(GetGraph()->GetIntConstant(masked_cst), /* index= */ 1); | 
| Aart Bik | 50e20d5 | 2017-05-05 14:07:29 -0700 | [diff] [blame] | 372 |       RecordSimplification(); | 
 | 373 |       return; | 
| Alexandre Rames | 5051844 | 2016-06-27 11:39:19 +0100 | [diff] [blame] | 374 |     } | 
 | 375 |   } | 
 | 376 |  | 
 | 377 |   // Shift operations implicitly mask the shift amount according to the type width. Get rid of | 
| Vladimir Marko | 7033d49 | 2017-09-28 16:32:24 +0100 | [diff] [blame] | 378 |   // unnecessary And/Or/Xor/Add/Sub/TypeConversion operations on the shift amount that do not | 
 | 379 |   // affect the relevant bits. | 
| Alexandre Rames | 5051844 | 2016-06-27 11:39:19 +0100 | [diff] [blame] | 380 |   // Replace code looking like | 
| Vladimir Marko | 7033d49 | 2017-09-28 16:32:24 +0100 | [diff] [blame] | 381 |   //    AND adjusted_shift, shift, <superset of implicit mask> | 
 | 382 |   //    [OR/XOR/ADD/SUB adjusted_shift, shift, <value not overlapping with implicit mask>] | 
 | 383 |   //    [<conversion-from-integral-non-64-bit-type> adjusted_shift, shift] | 
 | 384 |   //    SHL dst, value, adjusted_shift | 
| Alexandre Rames | 5051844 | 2016-06-27 11:39:19 +0100 | [diff] [blame] | 385 |   // with | 
 | 386 |   //    SHL dst, value, shift | 
| Vladimir Marko | 7033d49 | 2017-09-28 16:32:24 +0100 | [diff] [blame] | 387 |   if (shift_amount->IsAnd() || | 
 | 388 |       shift_amount->IsOr() || | 
 | 389 |       shift_amount->IsXor() || | 
 | 390 |       shift_amount->IsAdd() || | 
 | 391 |       shift_amount->IsSub()) { | 
 | 392 |     int64_t required_result = shift_amount->IsAnd() ? implicit_mask : 0; | 
 | 393 |     HBinaryOperation* bin_op = shift_amount->AsBinaryOperation(); | 
 | 394 |     HConstant* mask = bin_op->GetConstantRight(); | 
 | 395 |     if (mask != nullptr && (Int64FromConstant(mask) & implicit_mask) == required_result) { | 
 | 396 |       instruction->ReplaceInput(bin_op->GetLeastConstantLeft(), 1); | 
| Alexandre Rames | 5051844 | 2016-06-27 11:39:19 +0100 | [diff] [blame] | 397 |       RecordSimplification(); | 
| Vladimir Marko | 7033d49 | 2017-09-28 16:32:24 +0100 | [diff] [blame] | 398 |       return; | 
 | 399 |     } | 
 | 400 |   } else if (shift_amount->IsTypeConversion()) { | 
 | 401 |     DCHECK_NE(shift_amount->GetType(), DataType::Type::kBool);  // We never convert to bool. | 
 | 402 |     DataType::Type source_type = shift_amount->InputAt(0)->GetType(); | 
 | 403 |     // Non-integral and 64-bit source types require an explicit type conversion. | 
 | 404 |     if (DataType::IsIntegralType(source_type) && !DataType::Is64BitType(source_type)) { | 
 | 405 |       instruction->ReplaceInput(shift_amount->AsTypeConversion()->GetInput(), 1); | 
 | 406 |       RecordSimplification(); | 
 | 407 |       return; | 
| Mark Mendell | ba56d06 | 2015-05-05 21:34:03 -0400 | [diff] [blame] | 408 |     } | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 409 |   } | 
 | 410 | } | 
 | 411 |  | 
| Scott Wakeling | 40a04bf | 2015-12-11 09:50:36 +0000 | [diff] [blame] | 412 | static bool IsSubRegBitsMinusOther(HSub* sub, size_t reg_bits, HInstruction* other) { | 
 | 413 |   return (sub->GetRight() == other && | 
 | 414 |           sub->GetLeft()->IsConstant() && | 
 | 415 |           (Int64FromConstant(sub->GetLeft()->AsConstant()) & (reg_bits - 1)) == 0); | 
 | 416 | } | 
 | 417 |  | 
 | 418 | bool InstructionSimplifierVisitor::ReplaceRotateWithRor(HBinaryOperation* op, | 
 | 419 |                                                         HUShr* ushr, | 
 | 420 |                                                         HShl* shl) { | 
| Roland Levillain | 22c4922 | 2016-03-18 14:04:28 +0000 | [diff] [blame] | 421 |   DCHECK(op->IsAdd() || op->IsXor() || op->IsOr()) << op->DebugName(); | 
| Vladimir Marko | ca6fff8 | 2017-10-03 14:49:14 +0100 | [diff] [blame] | 422 |   HRor* ror = | 
 | 423 |       new (GetGraph()->GetAllocator()) HRor(ushr->GetType(), ushr->GetLeft(), ushr->GetRight()); | 
| Scott Wakeling | 40a04bf | 2015-12-11 09:50:36 +0000 | [diff] [blame] | 424 |   op->GetBlock()->ReplaceAndRemoveInstructionWith(op, ror); | 
 | 425 |   if (!ushr->HasUses()) { | 
 | 426 |     ushr->GetBlock()->RemoveInstruction(ushr); | 
 | 427 |   } | 
 | 428 |   if (!ushr->GetRight()->HasUses()) { | 
 | 429 |     ushr->GetRight()->GetBlock()->RemoveInstruction(ushr->GetRight()); | 
 | 430 |   } | 
 | 431 |   if (!shl->HasUses()) { | 
 | 432 |     shl->GetBlock()->RemoveInstruction(shl); | 
 | 433 |   } | 
 | 434 |   if (!shl->GetRight()->HasUses()) { | 
 | 435 |     shl->GetRight()->GetBlock()->RemoveInstruction(shl->GetRight()); | 
 | 436 |   } | 
| Alexandre Rames | c5809c3 | 2016-05-25 15:01:06 +0100 | [diff] [blame] | 437 |   RecordSimplification(); | 
| Scott Wakeling | 40a04bf | 2015-12-11 09:50:36 +0000 | [diff] [blame] | 438 |   return true; | 
 | 439 | } | 
 | 440 |  | 
 | 441 | // Try to replace a binary operation flanked by one UShr and one Shl with a bitfield rotation. | 
 | 442 | bool InstructionSimplifierVisitor::TryReplaceWithRotate(HBinaryOperation* op) { | 
| Scott Wakeling | 40a04bf | 2015-12-11 09:50:36 +0000 | [diff] [blame] | 443 |   DCHECK(op->IsAdd() || op->IsXor() || op->IsOr()); | 
 | 444 |   HInstruction* left = op->GetLeft(); | 
 | 445 |   HInstruction* right = op->GetRight(); | 
 | 446 |   // If we have an UShr and a Shl (in either order). | 
 | 447 |   if ((left->IsUShr() && right->IsShl()) || (left->IsShl() && right->IsUShr())) { | 
 | 448 |     HUShr* ushr = left->IsUShr() ? left->AsUShr() : right->AsUShr(); | 
 | 449 |     HShl* shl = left->IsShl() ? left->AsShl() : right->AsShl(); | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 450 |     DCHECK(DataType::IsIntOrLongType(ushr->GetType())); | 
| Scott Wakeling | 40a04bf | 2015-12-11 09:50:36 +0000 | [diff] [blame] | 451 |     if (ushr->GetType() == shl->GetType() && | 
 | 452 |         ushr->GetLeft() == shl->GetLeft()) { | 
 | 453 |       if (ushr->GetRight()->IsConstant() && shl->GetRight()->IsConstant()) { | 
 | 454 |         // Shift distances are both constant, try replacing with Ror if they | 
 | 455 |         // add up to the register size. | 
 | 456 |         return TryReplaceWithRotateConstantPattern(op, ushr, shl); | 
 | 457 |       } else if (ushr->GetRight()->IsSub() || shl->GetRight()->IsSub()) { | 
 | 458 |         // Shift distances are potentially of the form x and (reg_size - x). | 
 | 459 |         return TryReplaceWithRotateRegisterSubPattern(op, ushr, shl); | 
 | 460 |       } else if (ushr->GetRight()->IsNeg() || shl->GetRight()->IsNeg()) { | 
 | 461 |         // Shift distances are potentially of the form d and -d. | 
 | 462 |         return TryReplaceWithRotateRegisterNegPattern(op, ushr, shl); | 
 | 463 |       } | 
 | 464 |     } | 
 | 465 |   } | 
 | 466 |   return false; | 
 | 467 | } | 
 | 468 |  | 
 | 469 | // Try replacing code looking like (x >>> #rdist OP x << #ldist): | 
 | 470 | //    UShr dst, x,   #rdist | 
 | 471 | //    Shl  tmp, x,   #ldist | 
 | 472 | //    OP   dst, dst, tmp | 
 | 473 | // or like (x >>> #rdist OP x << #-ldist): | 
 | 474 | //    UShr dst, x,   #rdist | 
 | 475 | //    Shl  tmp, x,   #-ldist | 
 | 476 | //    OP   dst, dst, tmp | 
 | 477 | // with | 
 | 478 | //    Ror  dst, x,   #rdist | 
 | 479 | bool InstructionSimplifierVisitor::TryReplaceWithRotateConstantPattern(HBinaryOperation* op, | 
 | 480 |                                                                        HUShr* ushr, | 
 | 481 |                                                                        HShl* shl) { | 
 | 482 |   DCHECK(op->IsAdd() || op->IsXor() || op->IsOr()); | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 483 |   size_t reg_bits = DataType::Size(ushr->GetType()) * kBitsPerByte; | 
| Scott Wakeling | 40a04bf | 2015-12-11 09:50:36 +0000 | [diff] [blame] | 484 |   size_t rdist = Int64FromConstant(ushr->GetRight()->AsConstant()); | 
 | 485 |   size_t ldist = Int64FromConstant(shl->GetRight()->AsConstant()); | 
 | 486 |   if (((ldist + rdist) & (reg_bits - 1)) == 0) { | 
 | 487 |     ReplaceRotateWithRor(op, ushr, shl); | 
 | 488 |     return true; | 
 | 489 |   } | 
 | 490 |   return false; | 
 | 491 | } | 
 | 492 |  | 
 | 493 | // Replace code looking like (x >>> -d OP x << d): | 
 | 494 | //    Neg  neg, d | 
 | 495 | //    UShr dst, x,   neg | 
 | 496 | //    Shl  tmp, x,   d | 
 | 497 | //    OP   dst, dst, tmp | 
 | 498 | // with | 
 | 499 | //    Neg  neg, d | 
 | 500 | //    Ror  dst, x,   neg | 
 | 501 | // *** OR *** | 
 | 502 | // Replace code looking like (x >>> d OP x << -d): | 
 | 503 | //    UShr dst, x,   d | 
 | 504 | //    Neg  neg, d | 
 | 505 | //    Shl  tmp, x,   neg | 
 | 506 | //    OP   dst, dst, tmp | 
 | 507 | // with | 
 | 508 | //    Ror  dst, x,   d | 
 | 509 | bool InstructionSimplifierVisitor::TryReplaceWithRotateRegisterNegPattern(HBinaryOperation* op, | 
 | 510 |                                                                           HUShr* ushr, | 
 | 511 |                                                                           HShl* shl) { | 
 | 512 |   DCHECK(op->IsAdd() || op->IsXor() || op->IsOr()); | 
 | 513 |   DCHECK(ushr->GetRight()->IsNeg() || shl->GetRight()->IsNeg()); | 
 | 514 |   bool neg_is_left = shl->GetRight()->IsNeg(); | 
 | 515 |   HNeg* neg = neg_is_left ? shl->GetRight()->AsNeg() : ushr->GetRight()->AsNeg(); | 
 | 516 |   // And the shift distance being negated is the distance being shifted the other way. | 
 | 517 |   if (neg->InputAt(0) == (neg_is_left ? ushr->GetRight() : shl->GetRight())) { | 
 | 518 |     ReplaceRotateWithRor(op, ushr, shl); | 
 | 519 |   } | 
 | 520 |   return false; | 
 | 521 | } | 
 | 522 |  | 
 | 523 | // Try replacing code looking like (x >>> d OP x << (#bits - d)): | 
 | 524 | //    UShr dst, x,     d | 
 | 525 | //    Sub  ld,  #bits, d | 
 | 526 | //    Shl  tmp, x,     ld | 
 | 527 | //    OP   dst, dst,   tmp | 
 | 528 | // with | 
 | 529 | //    Ror  dst, x,     d | 
 | 530 | // *** OR *** | 
 | 531 | // Replace code looking like (x >>> (#bits - d) OP x << d): | 
 | 532 | //    Sub  rd,  #bits, d | 
 | 533 | //    UShr dst, x,     rd | 
 | 534 | //    Shl  tmp, x,     d | 
 | 535 | //    OP   dst, dst,   tmp | 
 | 536 | // with | 
 | 537 | //    Neg  neg, d | 
 | 538 | //    Ror  dst, x,     neg | 
 | 539 | bool InstructionSimplifierVisitor::TryReplaceWithRotateRegisterSubPattern(HBinaryOperation* op, | 
 | 540 |                                                                           HUShr* ushr, | 
 | 541 |                                                                           HShl* shl) { | 
 | 542 |   DCHECK(op->IsAdd() || op->IsXor() || op->IsOr()); | 
 | 543 |   DCHECK(ushr->GetRight()->IsSub() || shl->GetRight()->IsSub()); | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 544 |   size_t reg_bits = DataType::Size(ushr->GetType()) * kBitsPerByte; | 
| Scott Wakeling | 40a04bf | 2015-12-11 09:50:36 +0000 | [diff] [blame] | 545 |   HInstruction* shl_shift = shl->GetRight(); | 
 | 546 |   HInstruction* ushr_shift = ushr->GetRight(); | 
 | 547 |   if ((shl_shift->IsSub() && IsSubRegBitsMinusOther(shl_shift->AsSub(), reg_bits, ushr_shift)) || | 
 | 548 |       (ushr_shift->IsSub() && IsSubRegBitsMinusOther(ushr_shift->AsSub(), reg_bits, shl_shift))) { | 
 | 549 |     return ReplaceRotateWithRor(op, ushr, shl); | 
 | 550 |   } | 
 | 551 |   return false; | 
 | 552 | } | 
 | 553 |  | 
| Calin Juravle | 10e244f | 2015-01-26 18:54:32 +0000 | [diff] [blame] | 554 | void InstructionSimplifierVisitor::VisitNullCheck(HNullCheck* null_check) { | 
 | 555 |   HInstruction* obj = null_check->InputAt(0); | 
 | 556 |   if (!obj->CanBeNull()) { | 
 | 557 |     null_check->ReplaceWith(obj); | 
 | 558 |     null_check->GetBlock()->RemoveInstruction(null_check); | 
| Calin Juravle | acf735c | 2015-02-12 15:25:22 +0000 | [diff] [blame] | 559 |     if (stats_ != nullptr) { | 
 | 560 |       stats_->RecordStat(MethodCompilationStat::kRemovedNullCheck); | 
 | 561 |     } | 
 | 562 |   } | 
 | 563 | } | 
 | 564 |  | 
| Nicolas Geoffray | 6e7455e | 2015-09-28 16:25:37 +0100 | [diff] [blame] | 565 | bool InstructionSimplifierVisitor::CanEnsureNotNullAt(HInstruction* input, HInstruction* at) const { | 
 | 566 |   if (!input->CanBeNull()) { | 
 | 567 |     return true; | 
 | 568 |   } | 
 | 569 |  | 
| Vladimir Marko | 46817b8 | 2016-03-29 12:21:58 +0100 | [diff] [blame] | 570 |   for (const HUseListNode<HInstruction*>& use : input->GetUses()) { | 
 | 571 |     HInstruction* user = use.GetUser(); | 
 | 572 |     if (user->IsNullCheck() && user->StrictlyDominates(at)) { | 
| Guillaume "Vermeille" Sanchez | 8909baf | 2015-04-23 21:35:11 +0100 | [diff] [blame] | 573 |       return true; | 
 | 574 |     } | 
 | 575 |   } | 
| Nicolas Geoffray | 6e7455e | 2015-09-28 16:25:37 +0100 | [diff] [blame] | 576 |  | 
| Guillaume "Vermeille" Sanchez | 8909baf | 2015-04-23 21:35:11 +0100 | [diff] [blame] | 577 |   return false; | 
 | 578 | } | 
 | 579 |  | 
| Guillaume Sanchez | 222862c | 2015-06-09 18:33:02 +0100 | [diff] [blame] | 580 | // Returns whether doing a type test between the class of `object` against `klass` has | 
 | 581 | // a statically known outcome. The result of the test is stored in `outcome`. | 
| Vladimir Marko | 175e786 | 2018-03-27 09:03:13 +0000 | [diff] [blame] | 582 | static bool TypeCheckHasKnownOutcome(ReferenceTypeInfo class_rti, | 
 | 583 |                                      HInstruction* object, | 
 | 584 |                                      /*out*/bool* outcome) { | 
| Calin Juravle | 2e76830 | 2015-07-28 14:41:11 +0000 | [diff] [blame] | 585 |   DCHECK(!object->IsNullConstant()) << "Null constants should be special cased"; | 
 | 586 |   ReferenceTypeInfo obj_rti = object->GetReferenceTypeInfo(); | 
 | 587 |   ScopedObjectAccess soa(Thread::Current()); | 
 | 588 |   if (!obj_rti.IsValid()) { | 
 | 589 |     // We run the simplifier before the reference type propagation so type info might not be | 
 | 590 |     // available. | 
| Guillaume Sanchez | 222862c | 2015-06-09 18:33:02 +0100 | [diff] [blame] | 591 |     return false; | 
| Calin Juravle | acf735c | 2015-02-12 15:25:22 +0000 | [diff] [blame] | 592 |   } | 
| Guillaume Sanchez | 222862c | 2015-06-09 18:33:02 +0100 | [diff] [blame] | 593 |  | 
| Calin Juravle | 98893e1 | 2015-10-02 21:05:03 +0100 | [diff] [blame] | 594 |   if (!class_rti.IsValid()) { | 
 | 595 |     // Happens when the loaded class is unresolved. | 
 | 596 |     return false; | 
 | 597 |   } | 
 | 598 |   DCHECK(class_rti.IsExact()); | 
| Calin Juravle | acf735c | 2015-02-12 15:25:22 +0000 | [diff] [blame] | 599 |   if (class_rti.IsSupertypeOf(obj_rti)) { | 
| Guillaume Sanchez | 222862c | 2015-06-09 18:33:02 +0100 | [diff] [blame] | 600 |     *outcome = true; | 
 | 601 |     return true; | 
 | 602 |   } else if (obj_rti.IsExact()) { | 
 | 603 |     // The test failed at compile time so will also fail at runtime. | 
 | 604 |     *outcome = false; | 
 | 605 |     return true; | 
| Nicolas Geoffray | 7cb499b | 2015-06-17 11:35:11 +0100 | [diff] [blame] | 606 |   } else if (!class_rti.IsInterface() | 
 | 607 |              && !obj_rti.IsInterface() | 
 | 608 |              && !obj_rti.IsSupertypeOf(class_rti)) { | 
| Guillaume Sanchez | 222862c | 2015-06-09 18:33:02 +0100 | [diff] [blame] | 609 |     // Different type hierarchy. The test will fail. | 
 | 610 |     *outcome = false; | 
 | 611 |     return true; | 
 | 612 |   } | 
 | 613 |   return false; | 
 | 614 | } | 
 | 615 |  | 
 | 616 | void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) { | 
 | 617 |   HInstruction* object = check_cast->InputAt(0); | 
| Vladimir Marko | 175e786 | 2018-03-27 09:03:13 +0000 | [diff] [blame] | 618 |   if (check_cast->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck && | 
 | 619 |       check_cast->GetTargetClass()->NeedsAccessCheck()) { | 
| Calin Juravle | e53fb55 | 2015-10-07 17:51:52 +0100 | [diff] [blame] | 620 |     // If we need to perform an access check we cannot remove the instruction. | 
 | 621 |     return; | 
 | 622 |   } | 
 | 623 |  | 
| Nicolas Geoffray | 6e7455e | 2015-09-28 16:25:37 +0100 | [diff] [blame] | 624 |   if (CanEnsureNotNullAt(object, check_cast)) { | 
| Guillaume Sanchez | 222862c | 2015-06-09 18:33:02 +0100 | [diff] [blame] | 625 |     check_cast->ClearMustDoNullCheck(); | 
 | 626 |   } | 
 | 627 |  | 
 | 628 |   if (object->IsNullConstant()) { | 
| Calin Juravle | acf735c | 2015-02-12 15:25:22 +0000 | [diff] [blame] | 629 |     check_cast->GetBlock()->RemoveInstruction(check_cast); | 
| Igor Murashkin | 1e065a5 | 2017-08-09 13:20:34 -0700 | [diff] [blame] | 630 |     MaybeRecordStat(stats_, MethodCompilationStat::kRemovedCheckedCast); | 
| Guillaume Sanchez | 222862c | 2015-06-09 18:33:02 +0100 | [diff] [blame] | 631 |     return; | 
 | 632 |   } | 
 | 633 |  | 
| Roland Levillain | 05e34f4 | 2018-05-24 13:19:05 +0000 | [diff] [blame] | 634 |   // Historical note: The `outcome` was initialized to please Valgrind - the compiler can reorder | 
 | 635 |   // the return value check with the `outcome` check, b/27651442. | 
| Vladimir Marko | a65ed30 | 2016-03-14 21:21:29 +0000 | [diff] [blame] | 636 |   bool outcome = false; | 
| Vladimir Marko | 175e786 | 2018-03-27 09:03:13 +0000 | [diff] [blame] | 637 |   if (TypeCheckHasKnownOutcome(check_cast->GetTargetClassRTI(), object, &outcome)) { | 
| Guillaume Sanchez | 222862c | 2015-06-09 18:33:02 +0100 | [diff] [blame] | 638 |     if (outcome) { | 
 | 639 |       check_cast->GetBlock()->RemoveInstruction(check_cast); | 
| Igor Murashkin | 1e065a5 | 2017-08-09 13:20:34 -0700 | [diff] [blame] | 640 |       MaybeRecordStat(stats_, MethodCompilationStat::kRemovedCheckedCast); | 
| Vladimir Marko | 175e786 | 2018-03-27 09:03:13 +0000 | [diff] [blame] | 641 |       if (check_cast->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck) { | 
 | 642 |         HLoadClass* load_class = check_cast->GetTargetClass(); | 
 | 643 |         if (!load_class->HasUses()) { | 
 | 644 |           // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. | 
 | 645 |           // However, here we know that it cannot because the checkcast was successfull, hence | 
 | 646 |           // the class was already loaded. | 
 | 647 |           load_class->GetBlock()->RemoveInstruction(load_class); | 
 | 648 |         } | 
| Nicolas Geoffray | efa8468 | 2015-08-12 18:28:14 -0700 | [diff] [blame] | 649 |       } | 
| Guillaume Sanchez | 222862c | 2015-06-09 18:33:02 +0100 | [diff] [blame] | 650 |     } else { | 
 | 651 |       // Don't do anything for exceptional cases for now. Ideally we should remove | 
 | 652 |       // all instructions and blocks this instruction dominates. | 
 | 653 |     } | 
| Calin Juravle | 10e244f | 2015-01-26 18:54:32 +0000 | [diff] [blame] | 654 |   } | 
 | 655 | } | 
 | 656 |  | 
| Guillaume "Vermeille" Sanchez | af88835 | 2015-04-20 14:41:30 +0100 | [diff] [blame] | 657 | void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { | 
| Guillaume Sanchez | 222862c | 2015-06-09 18:33:02 +0100 | [diff] [blame] | 658 |   HInstruction* object = instruction->InputAt(0); | 
| Vladimir Marko | 175e786 | 2018-03-27 09:03:13 +0000 | [diff] [blame] | 659 |   if (instruction->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck && | 
 | 660 |       instruction->GetTargetClass()->NeedsAccessCheck()) { | 
| Calin Juravle | e53fb55 | 2015-10-07 17:51:52 +0100 | [diff] [blame] | 661 |     // If we need to perform an access check we cannot remove the instruction. | 
 | 662 |     return; | 
 | 663 |   } | 
 | 664 |  | 
| Guillaume Sanchez | 222862c | 2015-06-09 18:33:02 +0100 | [diff] [blame] | 665 |   bool can_be_null = true; | 
| Nicolas Geoffray | 6e7455e | 2015-09-28 16:25:37 +0100 | [diff] [blame] | 666 |   if (CanEnsureNotNullAt(object, instruction)) { | 
| Guillaume Sanchez | 222862c | 2015-06-09 18:33:02 +0100 | [diff] [blame] | 667 |     can_be_null = false; | 
| Guillaume "Vermeille" Sanchez | af88835 | 2015-04-20 14:41:30 +0100 | [diff] [blame] | 668 |     instruction->ClearMustDoNullCheck(); | 
 | 669 |   } | 
| Guillaume Sanchez | 222862c | 2015-06-09 18:33:02 +0100 | [diff] [blame] | 670 |  | 
 | 671 |   HGraph* graph = GetGraph(); | 
 | 672 |   if (object->IsNullConstant()) { | 
| Vladimir Marko | cd09e1f | 2017-11-24 15:02:40 +0000 | [diff] [blame] | 673 |     MaybeRecordStat(stats_, MethodCompilationStat::kRemovedInstanceOf); | 
| Guillaume Sanchez | 222862c | 2015-06-09 18:33:02 +0100 | [diff] [blame] | 674 |     instruction->ReplaceWith(graph->GetIntConstant(0)); | 
 | 675 |     instruction->GetBlock()->RemoveInstruction(instruction); | 
 | 676 |     RecordSimplification(); | 
 | 677 |     return; | 
 | 678 |   } | 
 | 679 |  | 
| Roland Levillain | 05e34f4 | 2018-05-24 13:19:05 +0000 | [diff] [blame] | 680 |   // Historical note: The `outcome` was initialized to please Valgrind - the compiler can reorder | 
 | 681 |   // the return value check with the `outcome` check, b/27651442. | 
| Vladimir Marko | 24bd895 | 2016-03-15 10:40:33 +0000 | [diff] [blame] | 682 |   bool outcome = false; | 
| Vladimir Marko | 175e786 | 2018-03-27 09:03:13 +0000 | [diff] [blame] | 683 |   if (TypeCheckHasKnownOutcome(instruction->GetTargetClassRTI(), object, &outcome)) { | 
| Vladimir Marko | cd09e1f | 2017-11-24 15:02:40 +0000 | [diff] [blame] | 684 |     MaybeRecordStat(stats_, MethodCompilationStat::kRemovedInstanceOf); | 
| Guillaume Sanchez | 222862c | 2015-06-09 18:33:02 +0100 | [diff] [blame] | 685 |     if (outcome && can_be_null) { | 
 | 686 |       // Type test will succeed, we just need a null test. | 
| Vladimir Marko | ca6fff8 | 2017-10-03 14:49:14 +0100 | [diff] [blame] | 687 |       HNotEqual* test = new (graph->GetAllocator()) HNotEqual(graph->GetNullConstant(), object); | 
| Guillaume Sanchez | 222862c | 2015-06-09 18:33:02 +0100 | [diff] [blame] | 688 |       instruction->GetBlock()->InsertInstructionBefore(test, instruction); | 
 | 689 |       instruction->ReplaceWith(test); | 
 | 690 |     } else { | 
 | 691 |       // We've statically determined the result of the instanceof. | 
 | 692 |       instruction->ReplaceWith(graph->GetIntConstant(outcome)); | 
 | 693 |     } | 
 | 694 |     RecordSimplification(); | 
 | 695 |     instruction->GetBlock()->RemoveInstruction(instruction); | 
| Vladimir Marko | 175e786 | 2018-03-27 09:03:13 +0000 | [diff] [blame] | 696 |     if (outcome && instruction->GetTypeCheckKind() != TypeCheckKind::kBitstringCheck) { | 
 | 697 |       HLoadClass* load_class = instruction->GetTargetClass(); | 
 | 698 |       if (!load_class->HasUses()) { | 
 | 699 |         // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. | 
 | 700 |         // However, here we know that it cannot because the instanceof check was successfull, hence | 
 | 701 |         // the class was already loaded. | 
 | 702 |         load_class->GetBlock()->RemoveInstruction(load_class); | 
 | 703 |       } | 
| Nicolas Geoffray | efa8468 | 2015-08-12 18:28:14 -0700 | [diff] [blame] | 704 |     } | 
| Guillaume Sanchez | 222862c | 2015-06-09 18:33:02 +0100 | [diff] [blame] | 705 |   } | 
| Guillaume "Vermeille" Sanchez | af88835 | 2015-04-20 14:41:30 +0100 | [diff] [blame] | 706 | } | 
 | 707 |  | 
| Nicolas Geoffray | 07276db | 2015-05-18 14:22:09 +0100 | [diff] [blame] | 708 | void InstructionSimplifierVisitor::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 709 |   if ((instruction->GetValue()->GetType() == DataType::Type::kReference) | 
| Nicolas Geoffray | 6e7455e | 2015-09-28 16:25:37 +0100 | [diff] [blame] | 710 |       && CanEnsureNotNullAt(instruction->GetValue(), instruction)) { | 
| Nicolas Geoffray | 07276db | 2015-05-18 14:22:09 +0100 | [diff] [blame] | 711 |     instruction->ClearValueCanBeNull(); | 
 | 712 |   } | 
 | 713 | } | 
 | 714 |  | 
 | 715 | void InstructionSimplifierVisitor::VisitStaticFieldSet(HStaticFieldSet* instruction) { | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 716 |   if ((instruction->GetValue()->GetType() == DataType::Type::kReference) | 
| Nicolas Geoffray | 6e7455e | 2015-09-28 16:25:37 +0100 | [diff] [blame] | 717 |       && CanEnsureNotNullAt(instruction->GetValue(), instruction)) { | 
| Nicolas Geoffray | 07276db | 2015-05-18 14:22:09 +0100 | [diff] [blame] | 718 |     instruction->ClearValueCanBeNull(); | 
 | 719 |   } | 
 | 720 | } | 
 | 721 |  | 
| Vladimir Marko | e764d2e | 2017-10-05 14:35:55 +0100 | [diff] [blame] | 722 | static HCondition* GetOppositeConditionSwapOps(ArenaAllocator* allocator, HInstruction* cond) { | 
| Anton Shamin | bdd7935 | 2016-02-15 12:48:36 +0600 | [diff] [blame] | 723 |   HInstruction *lhs = cond->InputAt(0); | 
 | 724 |   HInstruction *rhs = cond->InputAt(1); | 
 | 725 |   switch (cond->GetKind()) { | 
 | 726 |     case HInstruction::kEqual: | 
| Vladimir Marko | e764d2e | 2017-10-05 14:35:55 +0100 | [diff] [blame] | 727 |       return new (allocator) HEqual(rhs, lhs); | 
| Anton Shamin | bdd7935 | 2016-02-15 12:48:36 +0600 | [diff] [blame] | 728 |     case HInstruction::kNotEqual: | 
| Vladimir Marko | e764d2e | 2017-10-05 14:35:55 +0100 | [diff] [blame] | 729 |       return new (allocator) HNotEqual(rhs, lhs); | 
| Anton Shamin | bdd7935 | 2016-02-15 12:48:36 +0600 | [diff] [blame] | 730 |     case HInstruction::kLessThan: | 
| Vladimir Marko | e764d2e | 2017-10-05 14:35:55 +0100 | [diff] [blame] | 731 |       return new (allocator) HGreaterThan(rhs, lhs); | 
| Anton Shamin | bdd7935 | 2016-02-15 12:48:36 +0600 | [diff] [blame] | 732 |     case HInstruction::kLessThanOrEqual: | 
| Vladimir Marko | e764d2e | 2017-10-05 14:35:55 +0100 | [diff] [blame] | 733 |       return new (allocator) HGreaterThanOrEqual(rhs, lhs); | 
| Anton Shamin | bdd7935 | 2016-02-15 12:48:36 +0600 | [diff] [blame] | 734 |     case HInstruction::kGreaterThan: | 
| Vladimir Marko | e764d2e | 2017-10-05 14:35:55 +0100 | [diff] [blame] | 735 |       return new (allocator) HLessThan(rhs, lhs); | 
| Anton Shamin | bdd7935 | 2016-02-15 12:48:36 +0600 | [diff] [blame] | 736 |     case HInstruction::kGreaterThanOrEqual: | 
| Vladimir Marko | e764d2e | 2017-10-05 14:35:55 +0100 | [diff] [blame] | 737 |       return new (allocator) HLessThanOrEqual(rhs, lhs); | 
| Anton Shamin | bdd7935 | 2016-02-15 12:48:36 +0600 | [diff] [blame] | 738 |     case HInstruction::kBelow: | 
| Vladimir Marko | e764d2e | 2017-10-05 14:35:55 +0100 | [diff] [blame] | 739 |       return new (allocator) HAbove(rhs, lhs); | 
| Anton Shamin | bdd7935 | 2016-02-15 12:48:36 +0600 | [diff] [blame] | 740 |     case HInstruction::kBelowOrEqual: | 
| Vladimir Marko | e764d2e | 2017-10-05 14:35:55 +0100 | [diff] [blame] | 741 |       return new (allocator) HAboveOrEqual(rhs, lhs); | 
| Anton Shamin | bdd7935 | 2016-02-15 12:48:36 +0600 | [diff] [blame] | 742 |     case HInstruction::kAbove: | 
| Vladimir Marko | e764d2e | 2017-10-05 14:35:55 +0100 | [diff] [blame] | 743 |       return new (allocator) HBelow(rhs, lhs); | 
| Anton Shamin | bdd7935 | 2016-02-15 12:48:36 +0600 | [diff] [blame] | 744 |     case HInstruction::kAboveOrEqual: | 
| Vladimir Marko | e764d2e | 2017-10-05 14:35:55 +0100 | [diff] [blame] | 745 |       return new (allocator) HBelowOrEqual(rhs, lhs); | 
| Anton Shamin | bdd7935 | 2016-02-15 12:48:36 +0600 | [diff] [blame] | 746 |     default: | 
 | 747 |       LOG(FATAL) << "Unknown ConditionType " << cond->GetKind(); | 
| Elliott Hughes | c1896c9 | 2018-11-29 11:33:18 -0800 | [diff] [blame] | 748 |       UNREACHABLE(); | 
| Anton Shamin | bdd7935 | 2016-02-15 12:48:36 +0600 | [diff] [blame] | 749 |   } | 
| Anton Shamin | bdd7935 | 2016-02-15 12:48:36 +0600 | [diff] [blame] | 750 | } | 
 | 751 |  | 
| Nicolas Geoffray | 5e6916c | 2014-11-18 16:53:35 +0000 | [diff] [blame] | 752 | void InstructionSimplifierVisitor::VisitEqual(HEqual* equal) { | 
| David Brazdil | 0d13fee | 2015-04-17 14:52:19 +0100 | [diff] [blame] | 753 |   HInstruction* input_const = equal->GetConstantRight(); | 
 | 754 |   if (input_const != nullptr) { | 
 | 755 |     HInstruction* input_value = equal->GetLeastConstantLeft(); | 
| Nicolas Geoffray | eb104c8 | 2019-06-03 17:58:34 +0100 | [diff] [blame] | 756 |     if ((input_value->GetType() == DataType::Type::kBool) && input_const->IsIntConstant()) { | 
| David Brazdil | 0d13fee | 2015-04-17 14:52:19 +0100 | [diff] [blame] | 757 |       HBasicBlock* block = equal->GetBlock(); | 
| Nicolas Geoffray | 3c4ab80 | 2015-06-19 11:42:07 +0100 | [diff] [blame] | 758 |       // We are comparing the boolean to a constant which is of type int and can | 
 | 759 |       // be any constant. | 
| Roland Levillain | 1a65388 | 2016-03-18 18:05:57 +0000 | [diff] [blame] | 760 |       if (input_const->AsIntConstant()->IsTrue()) { | 
| David Brazdil | 0d13fee | 2015-04-17 14:52:19 +0100 | [diff] [blame] | 761 |         // Replace (bool_value == true) with bool_value | 
 | 762 |         equal->ReplaceWith(input_value); | 
 | 763 |         block->RemoveInstruction(equal); | 
 | 764 |         RecordSimplification(); | 
| Roland Levillain | 1a65388 | 2016-03-18 18:05:57 +0000 | [diff] [blame] | 765 |       } else if (input_const->AsIntConstant()->IsFalse()) { | 
| Aart Bik | 2767f4b | 2016-10-28 15:03:53 -0700 | [diff] [blame] | 766 |         // Replace (bool_value == false) with !bool_value | 
| Mark Mendell | f652917 | 2015-11-17 11:16:56 -0500 | [diff] [blame] | 767 |         equal->ReplaceWith(GetGraph()->InsertOppositeCondition(input_value, equal)); | 
 | 768 |         block->RemoveInstruction(equal); | 
| David Brazdil | 0d13fee | 2015-04-17 14:52:19 +0100 | [diff] [blame] | 769 |         RecordSimplification(); | 
| David Brazdil | 1e9ec05 | 2015-06-22 10:26:45 +0100 | [diff] [blame] | 770 |       } else { | 
 | 771 |         // Replace (bool_value == integer_not_zero_nor_one_constant) with false | 
 | 772 |         equal->ReplaceWith(GetGraph()->GetIntConstant(0)); | 
 | 773 |         block->RemoveInstruction(equal); | 
 | 774 |         RecordSimplification(); | 
| David Brazdil | 0d13fee | 2015-04-17 14:52:19 +0100 | [diff] [blame] | 775 |       } | 
| Mark Mendell | c470193 | 2015-04-10 13:18:51 -0400 | [diff] [blame] | 776 |     } else { | 
 | 777 |       VisitCondition(equal); | 
| Nicolas Geoffray | 01ef345 | 2014-10-01 11:32:17 +0100 | [diff] [blame] | 778 |     } | 
| Mark Mendell | c470193 | 2015-04-10 13:18:51 -0400 | [diff] [blame] | 779 |   } else { | 
 | 780 |     VisitCondition(equal); | 
| Nicolas Geoffray | 01ef345 | 2014-10-01 11:32:17 +0100 | [diff] [blame] | 781 |   } | 
 | 782 | } | 
 | 783 |  | 
| David Brazdil | 0d13fee | 2015-04-17 14:52:19 +0100 | [diff] [blame] | 784 | void InstructionSimplifierVisitor::VisitNotEqual(HNotEqual* not_equal) { | 
 | 785 |   HInstruction* input_const = not_equal->GetConstantRight(); | 
 | 786 |   if (input_const != nullptr) { | 
 | 787 |     HInstruction* input_value = not_equal->GetLeastConstantLeft(); | 
| Nicolas Geoffray | eb104c8 | 2019-06-03 17:58:34 +0100 | [diff] [blame] | 788 |     if ((input_value->GetType() == DataType::Type::kBool) && input_const->IsIntConstant()) { | 
| David Brazdil | 0d13fee | 2015-04-17 14:52:19 +0100 | [diff] [blame] | 789 |       HBasicBlock* block = not_equal->GetBlock(); | 
| Nicolas Geoffray | 3c4ab80 | 2015-06-19 11:42:07 +0100 | [diff] [blame] | 790 |       // We are comparing the boolean to a constant which is of type int and can | 
 | 791 |       // be any constant. | 
| Roland Levillain | 1a65388 | 2016-03-18 18:05:57 +0000 | [diff] [blame] | 792 |       if (input_const->AsIntConstant()->IsTrue()) { | 
| Aart Bik | 2767f4b | 2016-10-28 15:03:53 -0700 | [diff] [blame] | 793 |         // Replace (bool_value != true) with !bool_value | 
| Mark Mendell | f652917 | 2015-11-17 11:16:56 -0500 | [diff] [blame] | 794 |         not_equal->ReplaceWith(GetGraph()->InsertOppositeCondition(input_value, not_equal)); | 
 | 795 |         block->RemoveInstruction(not_equal); | 
| David Brazdil | 0d13fee | 2015-04-17 14:52:19 +0100 | [diff] [blame] | 796 |         RecordSimplification(); | 
| Roland Levillain | 1a65388 | 2016-03-18 18:05:57 +0000 | [diff] [blame] | 797 |       } else if (input_const->AsIntConstant()->IsFalse()) { | 
| David Brazdil | 0d13fee | 2015-04-17 14:52:19 +0100 | [diff] [blame] | 798 |         // Replace (bool_value != false) with bool_value | 
| David Brazdil | 0d13fee | 2015-04-17 14:52:19 +0100 | [diff] [blame] | 799 |         not_equal->ReplaceWith(input_value); | 
 | 800 |         block->RemoveInstruction(not_equal); | 
 | 801 |         RecordSimplification(); | 
| David Brazdil | 1e9ec05 | 2015-06-22 10:26:45 +0100 | [diff] [blame] | 802 |       } else { | 
 | 803 |         // Replace (bool_value != integer_not_zero_nor_one_constant) with true | 
 | 804 |         not_equal->ReplaceWith(GetGraph()->GetIntConstant(1)); | 
 | 805 |         block->RemoveInstruction(not_equal); | 
 | 806 |         RecordSimplification(); | 
| David Brazdil | 0d13fee | 2015-04-17 14:52:19 +0100 | [diff] [blame] | 807 |       } | 
| Mark Mendell | c470193 | 2015-04-10 13:18:51 -0400 | [diff] [blame] | 808 |     } else { | 
 | 809 |       VisitCondition(not_equal); | 
| David Brazdil | 0d13fee | 2015-04-17 14:52:19 +0100 | [diff] [blame] | 810 |     } | 
| Mark Mendell | c470193 | 2015-04-10 13:18:51 -0400 | [diff] [blame] | 811 |   } else { | 
 | 812 |     VisitCondition(not_equal); | 
| David Brazdil | 0d13fee | 2015-04-17 14:52:19 +0100 | [diff] [blame] | 813 |   } | 
 | 814 | } | 
 | 815 |  | 
 | 816 | void InstructionSimplifierVisitor::VisitBooleanNot(HBooleanNot* bool_not) { | 
| David Brazdil | 74eb1b2 | 2015-12-14 11:44:01 +0000 | [diff] [blame] | 817 |   HInstruction* input = bool_not->InputAt(0); | 
 | 818 |   HInstruction* replace_with = nullptr; | 
 | 819 |  | 
 | 820 |   if (input->IsIntConstant()) { | 
 | 821 |     // Replace !(true/false) with false/true. | 
| Roland Levillain | 1a65388 | 2016-03-18 18:05:57 +0000 | [diff] [blame] | 822 |     if (input->AsIntConstant()->IsTrue()) { | 
| David Brazdil | 74eb1b2 | 2015-12-14 11:44:01 +0000 | [diff] [blame] | 823 |       replace_with = GetGraph()->GetIntConstant(0); | 
 | 824 |     } else { | 
| Roland Levillain | 1a65388 | 2016-03-18 18:05:57 +0000 | [diff] [blame] | 825 |       DCHECK(input->AsIntConstant()->IsFalse()) << input->AsIntConstant()->GetValue(); | 
| David Brazdil | 74eb1b2 | 2015-12-14 11:44:01 +0000 | [diff] [blame] | 826 |       replace_with = GetGraph()->GetIntConstant(1); | 
 | 827 |     } | 
 | 828 |   } else if (input->IsBooleanNot()) { | 
 | 829 |     // Replace (!(!bool_value)) with bool_value. | 
 | 830 |     replace_with = input->InputAt(0); | 
 | 831 |   } else if (input->IsCondition() && | 
 | 832 |              // Don't change FP compares. The definition of compares involving | 
 | 833 |              // NaNs forces the compares to be done as written by the user. | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 834 |              !DataType::IsFloatingPointType(input->InputAt(0)->GetType())) { | 
| David Brazdil | 74eb1b2 | 2015-12-14 11:44:01 +0000 | [diff] [blame] | 835 |     // Replace condition with its opposite. | 
 | 836 |     replace_with = GetGraph()->InsertOppositeCondition(input->AsCondition(), bool_not); | 
 | 837 |   } | 
 | 838 |  | 
 | 839 |   if (replace_with != nullptr) { | 
 | 840 |     bool_not->ReplaceWith(replace_with); | 
| David Brazdil | 0d13fee | 2015-04-17 14:52:19 +0100 | [diff] [blame] | 841 |     bool_not->GetBlock()->RemoveInstruction(bool_not); | 
| David Brazdil | 74eb1b2 | 2015-12-14 11:44:01 +0000 | [diff] [blame] | 842 |     RecordSimplification(); | 
 | 843 |   } | 
 | 844 | } | 
 | 845 |  | 
| Aart Bik | 4f7dd34 | 2017-09-12 13:12:57 -0700 | [diff] [blame] | 846 | // Constructs a new ABS(x) node in the HIR. | 
| Vladimir Marko | e764d2e | 2017-10-05 14:35:55 +0100 | [diff] [blame] | 847 | static HInstruction* NewIntegralAbs(ArenaAllocator* allocator, | 
 | 848 |                                     HInstruction* x, | 
 | 849 |                                     HInstruction* cursor) { | 
| Aart Bik | 2286da2 | 2018-03-22 10:50:22 -0700 | [diff] [blame] | 850 |   DataType::Type type = DataType::Kind(x->GetType()); | 
| Aart Bik | 3dad341 | 2018-02-28 12:01:46 -0800 | [diff] [blame] | 851 |   DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64); | 
| Aart Bik | 142b913 | 2018-03-14 15:12:59 -0700 | [diff] [blame] | 852 |   HAbs* abs = new (allocator) HAbs(type, x, cursor->GetDexPc()); | 
| Aart Bik | 3dad341 | 2018-02-28 12:01:46 -0800 | [diff] [blame] | 853 |   cursor->GetBlock()->InsertInstructionBefore(abs, cursor); | 
 | 854 |   return abs; | 
| Aart Bik | 4f7dd34 | 2017-09-12 13:12:57 -0700 | [diff] [blame] | 855 | } | 
 | 856 |  | 
| Aart Bik | 142b913 | 2018-03-14 15:12:59 -0700 | [diff] [blame] | 857 | // Constructs a new MIN/MAX(x, y) node in the HIR. | 
 | 858 | static HInstruction* NewIntegralMinMax(ArenaAllocator* allocator, | 
 | 859 |                                        HInstruction* x, | 
 | 860 |                                        HInstruction* y, | 
 | 861 |                                        HInstruction* cursor, | 
 | 862 |                                        bool is_min) { | 
| Aart Bik | 2286da2 | 2018-03-22 10:50:22 -0700 | [diff] [blame] | 863 |   DataType::Type type = DataType::Kind(x->GetType()); | 
| Aart Bik | 142b913 | 2018-03-14 15:12:59 -0700 | [diff] [blame] | 864 |   DCHECK(type == DataType::Type::kInt32 || type == DataType::Type::kInt64); | 
 | 865 |   HBinaryOperation* minmax = nullptr; | 
 | 866 |   if (is_min) { | 
 | 867 |     minmax = new (allocator) HMin(type, x, y, cursor->GetDexPc()); | 
 | 868 |   } else { | 
 | 869 |     minmax = new (allocator) HMax(type, x, y, cursor->GetDexPc()); | 
 | 870 |   } | 
 | 871 |   cursor->GetBlock()->InsertInstructionBefore(minmax, cursor); | 
 | 872 |   return minmax; | 
 | 873 | } | 
 | 874 |  | 
| Aart Bik | 4f7dd34 | 2017-09-12 13:12:57 -0700 | [diff] [blame] | 875 | // Returns true if operands a and b consists of widening type conversions | 
 | 876 | // (either explicit or implicit) to the given to_type. | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 877 | static bool AreLowerPrecisionArgs(DataType::Type to_type, HInstruction* a, HInstruction* b) { | 
| Aart Bik | 4f7dd34 | 2017-09-12 13:12:57 -0700 | [diff] [blame] | 878 |   if (a->IsTypeConversion() && a->GetType() == to_type) { | 
 | 879 |     a = a->InputAt(0); | 
 | 880 |   } | 
 | 881 |   if (b->IsTypeConversion() && b->GetType() == to_type) { | 
 | 882 |     b = b->InputAt(0); | 
 | 883 |   } | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 884 |   DataType::Type type1 = a->GetType(); | 
 | 885 |   DataType::Type type2 = b->GetType(); | 
| Vladimir Marko | d5d2f2c | 2017-09-26 12:37:26 +0100 | [diff] [blame] | 886 |   return (type1 == DataType::Type::kUint8  && type2 == DataType::Type::kUint8) || | 
 | 887 |          (type1 == DataType::Type::kInt8   && type2 == DataType::Type::kInt8) || | 
 | 888 |          (type1 == DataType::Type::kInt16  && type2 == DataType::Type::kInt16) || | 
 | 889 |          (type1 == DataType::Type::kUint16 && type2 == DataType::Type::kUint16) || | 
 | 890 |          (type1 == DataType::Type::kInt32  && type2 == DataType::Type::kInt32 && | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 891 |           to_type == DataType::Type::kInt64); | 
| Aart Bik | 4f7dd34 | 2017-09-12 13:12:57 -0700 | [diff] [blame] | 892 | } | 
 | 893 |  | 
| Aart Bik | 1d746de | 2018-03-28 16:30:02 -0700 | [diff] [blame] | 894 | // Returns an acceptable substitution for "a" on the select | 
 | 895 | // construct "a <cmp> b ? c : .."  during MIN/MAX recognition. | 
 | 896 | static HInstruction* AllowInMinMax(IfCondition cmp, | 
 | 897 |                                    HInstruction* a, | 
 | 898 |                                    HInstruction* b, | 
 | 899 |                                    HInstruction* c) { | 
 | 900 |   int64_t value = 0; | 
 | 901 |   if (IsInt64AndGet(b, /*out*/ &value) && | 
 | 902 |       (((cmp == kCondLT || cmp == kCondLE) && c->IsMax()) || | 
 | 903 |        ((cmp == kCondGT || cmp == kCondGE) && c->IsMin()))) { | 
 | 904 |     HConstant* other = c->AsBinaryOperation()->GetConstantRight(); | 
 | 905 |     if (other != nullptr && a == c->AsBinaryOperation()->GetLeastConstantLeft()) { | 
 | 906 |       int64_t other_value = Int64FromConstant(other); | 
 | 907 |       bool is_max = (cmp == kCondLT || cmp == kCondLE); | 
 | 908 |       // Allow the max for a <  100 ? max(a, -100) : .. | 
 | 909 |       //    or the min for a > -100 ? min(a,  100) : .. | 
 | 910 |       if (is_max ? (value >= other_value) : (value <= other_value)) { | 
 | 911 |         return c; | 
 | 912 |       } | 
 | 913 |     } | 
 | 914 |   } | 
 | 915 |   return nullptr; | 
 | 916 | } | 
 | 917 |  | 
| David Brazdil | 74eb1b2 | 2015-12-14 11:44:01 +0000 | [diff] [blame] | 918 | void InstructionSimplifierVisitor::VisitSelect(HSelect* select) { | 
 | 919 |   HInstruction* replace_with = nullptr; | 
 | 920 |   HInstruction* condition = select->GetCondition(); | 
 | 921 |   HInstruction* true_value = select->GetTrueValue(); | 
 | 922 |   HInstruction* false_value = select->GetFalseValue(); | 
 | 923 |  | 
 | 924 |   if (condition->IsBooleanNot()) { | 
 | 925 |     // Change ((!cond) ? x : y) to (cond ? y : x). | 
 | 926 |     condition = condition->InputAt(0); | 
 | 927 |     std::swap(true_value, false_value); | 
 | 928 |     select->ReplaceInput(false_value, 0); | 
 | 929 |     select->ReplaceInput(true_value, 1); | 
 | 930 |     select->ReplaceInput(condition, 2); | 
 | 931 |     RecordSimplification(); | 
 | 932 |   } | 
 | 933 |  | 
 | 934 |   if (true_value == false_value) { | 
 | 935 |     // Replace (cond ? x : x) with (x). | 
 | 936 |     replace_with = true_value; | 
 | 937 |   } else if (condition->IsIntConstant()) { | 
| Roland Levillain | 1a65388 | 2016-03-18 18:05:57 +0000 | [diff] [blame] | 938 |     if (condition->AsIntConstant()->IsTrue()) { | 
| David Brazdil | 74eb1b2 | 2015-12-14 11:44:01 +0000 | [diff] [blame] | 939 |       // Replace (true ? x : y) with (x). | 
 | 940 |       replace_with = true_value; | 
 | 941 |     } else { | 
 | 942 |       // Replace (false ? x : y) with (y). | 
| Roland Levillain | 1a65388 | 2016-03-18 18:05:57 +0000 | [diff] [blame] | 943 |       DCHECK(condition->AsIntConstant()->IsFalse()) << condition->AsIntConstant()->GetValue(); | 
| David Brazdil | 74eb1b2 | 2015-12-14 11:44:01 +0000 | [diff] [blame] | 944 |       replace_with = false_value; | 
 | 945 |     } | 
 | 946 |   } else if (true_value->IsIntConstant() && false_value->IsIntConstant()) { | 
| Roland Levillain | 1a65388 | 2016-03-18 18:05:57 +0000 | [diff] [blame] | 947 |     if (true_value->AsIntConstant()->IsTrue() && false_value->AsIntConstant()->IsFalse()) { | 
| David Brazdil | 74eb1b2 | 2015-12-14 11:44:01 +0000 | [diff] [blame] | 948 |       // Replace (cond ? true : false) with (cond). | 
 | 949 |       replace_with = condition; | 
| Roland Levillain | 1a65388 | 2016-03-18 18:05:57 +0000 | [diff] [blame] | 950 |     } else if (true_value->AsIntConstant()->IsFalse() && false_value->AsIntConstant()->IsTrue()) { | 
| David Brazdil | 74eb1b2 | 2015-12-14 11:44:01 +0000 | [diff] [blame] | 951 |       // Replace (cond ? false : true) with (!cond). | 
 | 952 |       replace_with = GetGraph()->InsertOppositeCondition(condition, select); | 
 | 953 |     } | 
| Aart Bik | 4f7dd34 | 2017-09-12 13:12:57 -0700 | [diff] [blame] | 954 |   } else if (condition->IsCondition()) { | 
 | 955 |     IfCondition cmp = condition->AsCondition()->GetCondition(); | 
 | 956 |     HInstruction* a = condition->InputAt(0); | 
 | 957 |     HInstruction* b = condition->InputAt(1); | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 958 |     DataType::Type t_type = true_value->GetType(); | 
 | 959 |     DataType::Type f_type = false_value->GetType(); | 
| Aart Bik | 4f7dd34 | 2017-09-12 13:12:57 -0700 | [diff] [blame] | 960 |     // Here we have a <cmp> b ? true_value : false_value. | 
| Aart Bik | 1d746de | 2018-03-28 16:30:02 -0700 | [diff] [blame] | 961 |     // Test if both values are compatible integral types (resulting MIN/MAX/ABS | 
 | 962 |     // type will be int or long, like the condition). Replacements are general, | 
 | 963 |     // but assume conditions prefer constants on the right. | 
| Aart Bik | 2286da2 | 2018-03-22 10:50:22 -0700 | [diff] [blame] | 964 |     if (DataType::IsIntegralType(t_type) && DataType::Kind(t_type) == DataType::Kind(f_type)) { | 
| Aart Bik | 1d746de | 2018-03-28 16:30:02 -0700 | [diff] [blame] | 965 |       // Allow a <  100 ? max(a, -100) : .. | 
 | 966 |       //    or a > -100 ? min(a,  100) : .. | 
 | 967 |       // to use min/max instead of a to detect nested min/max expressions. | 
 | 968 |       HInstruction* new_a = AllowInMinMax(cmp, a, b, true_value); | 
 | 969 |       if (new_a != nullptr) { | 
 | 970 |         a = new_a; | 
 | 971 |       } | 
| Aart Bik | 142b913 | 2018-03-14 15:12:59 -0700 | [diff] [blame] | 972 |       // Try to replace typical integral MIN/MAX/ABS constructs. | 
 | 973 |       if ((cmp == kCondLT || cmp == kCondLE || cmp == kCondGT || cmp == kCondGE) && | 
 | 974 |           ((a == true_value && b == false_value) || | 
 | 975 |            (b == true_value && a == false_value))) { | 
 | 976 |         // Found a < b ? a : b (MIN) or a < b ? b : a (MAX) | 
 | 977 |         //    or a > b ? a : b (MAX) or a > b ? b : a (MIN). | 
 | 978 |         bool is_min = (cmp == kCondLT || cmp == kCondLE) == (a == true_value); | 
 | 979 |         replace_with = NewIntegralMinMax(GetGraph()->GetAllocator(), a, b, select, is_min); | 
| Aart Bik | 1d746de | 2018-03-28 16:30:02 -0700 | [diff] [blame] | 980 |       } else if (((cmp == kCondLT || cmp == kCondLE) && true_value->IsNeg()) || | 
 | 981 |                  ((cmp == kCondGT || cmp == kCondGE) && false_value->IsNeg())) { | 
 | 982 |         bool negLeft = (cmp == kCondLT || cmp == kCondLE); | 
 | 983 |         HInstruction* the_negated = negLeft ? true_value->InputAt(0) : false_value->InputAt(0); | 
 | 984 |         HInstruction* not_negated = negLeft ? false_value : true_value; | 
 | 985 |         if (a == the_negated && a == not_negated && IsInt64Value(b, 0)) { | 
 | 986 |           // Found a < 0 ? -a :  a | 
 | 987 |           //    or a > 0 ?  a : -a | 
 | 988 |           // which can be replaced by ABS(a). | 
 | 989 |           replace_with = NewIntegralAbs(GetGraph()->GetAllocator(), a, select); | 
| Aart Bik | 4f7dd34 | 2017-09-12 13:12:57 -0700 | [diff] [blame] | 990 |         } | 
 | 991 |       } else if (true_value->IsSub() && false_value->IsSub()) { | 
 | 992 |         HInstruction* true_sub1 = true_value->InputAt(0); | 
 | 993 |         HInstruction* true_sub2 = true_value->InputAt(1); | 
 | 994 |         HInstruction* false_sub1 = false_value->InputAt(0); | 
 | 995 |         HInstruction* false_sub2 = false_value->InputAt(1); | 
 | 996 |         if ((((cmp == kCondGT || cmp == kCondGE) && | 
 | 997 |               (a == true_sub1 && b == true_sub2 && a == false_sub2 && b == false_sub1)) || | 
 | 998 |              ((cmp == kCondLT || cmp == kCondLE) && | 
 | 999 |               (a == true_sub2 && b == true_sub1 && a == false_sub1 && b == false_sub2))) && | 
 | 1000 |             AreLowerPrecisionArgs(t_type, a, b)) { | 
| Aart Bik | 1d746de | 2018-03-28 16:30:02 -0700 | [diff] [blame] | 1001 |           // Found a > b ? a - b  : b - a | 
 | 1002 |           //    or a < b ? b - a  : a - b | 
| Aart Bik | 4f7dd34 | 2017-09-12 13:12:57 -0700 | [diff] [blame] | 1003 |           // which can be replaced by ABS(a - b) for lower precision operands a, b. | 
| Vladimir Marko | ca6fff8 | 2017-10-03 14:49:14 +0100 | [diff] [blame] | 1004 |           replace_with = NewIntegralAbs(GetGraph()->GetAllocator(), true_value, select); | 
| Aart Bik | 4f7dd34 | 2017-09-12 13:12:57 -0700 | [diff] [blame] | 1005 |         } | 
 | 1006 |       } | 
 | 1007 |     } | 
| David Brazdil | 74eb1b2 | 2015-12-14 11:44:01 +0000 | [diff] [blame] | 1008 |   } | 
 | 1009 |  | 
 | 1010 |   if (replace_with != nullptr) { | 
 | 1011 |     select->ReplaceWith(replace_with); | 
 | 1012 |     select->GetBlock()->RemoveInstruction(select); | 
 | 1013 |     RecordSimplification(); | 
 | 1014 |   } | 
 | 1015 | } | 
 | 1016 |  | 
 | 1017 | void InstructionSimplifierVisitor::VisitIf(HIf* instruction) { | 
 | 1018 |   HInstruction* condition = instruction->InputAt(0); | 
 | 1019 |   if (condition->IsBooleanNot()) { | 
 | 1020 |     // Swap successors if input is negated. | 
 | 1021 |     instruction->ReplaceInput(condition->InputAt(0), 0); | 
 | 1022 |     instruction->GetBlock()->SwapSuccessors(); | 
| David Brazdil | 0d13fee | 2015-04-17 14:52:19 +0100 | [diff] [blame] | 1023 |     RecordSimplification(); | 
 | 1024 |   } | 
 | 1025 | } | 
 | 1026 |  | 
| Mingyao Yang | 0304e18 | 2015-01-30 16:41:29 -0800 | [diff] [blame] | 1027 | void InstructionSimplifierVisitor::VisitArrayLength(HArrayLength* instruction) { | 
 | 1028 |   HInstruction* input = instruction->InputAt(0); | 
 | 1029 |   // If the array is a NewArray with constant size, replace the array length | 
 | 1030 |   // with the constant instruction. This helps the bounds check elimination phase. | 
 | 1031 |   if (input->IsNewArray()) { | 
| Nicolas Geoffray | e761bcc | 2017-01-19 08:59:37 +0000 | [diff] [blame] | 1032 |     input = input->AsNewArray()->GetLength(); | 
| Mingyao Yang | 0304e18 | 2015-01-30 16:41:29 -0800 | [diff] [blame] | 1033 |     if (input->IsIntConstant()) { | 
 | 1034 |       instruction->ReplaceWith(input); | 
 | 1035 |     } | 
 | 1036 |   } | 
 | 1037 | } | 
 | 1038 |  | 
| Nicolas Geoffray | 5e6916c | 2014-11-18 16:53:35 +0000 | [diff] [blame] | 1039 | void InstructionSimplifierVisitor::VisitArraySet(HArraySet* instruction) { | 
| Nicolas Geoffray | af07bc1 | 2014-11-12 18:08:09 +0000 | [diff] [blame] | 1040 |   HInstruction* value = instruction->GetValue(); | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 1041 |   if (value->GetType() != DataType::Type::kReference) { | 
 | 1042 |     return; | 
 | 1043 |   } | 
| Nicolas Geoffray | af07bc1 | 2014-11-12 18:08:09 +0000 | [diff] [blame] | 1044 |  | 
| Nicolas Geoffray | e0395dd | 2015-09-25 11:04:45 +0100 | [diff] [blame] | 1045 |   if (CanEnsureNotNullAt(value, instruction)) { | 
 | 1046 |     instruction->ClearValueCanBeNull(); | 
 | 1047 |   } | 
 | 1048 |  | 
| Nicolas Geoffray | af07bc1 | 2014-11-12 18:08:09 +0000 | [diff] [blame] | 1049 |   if (value->IsArrayGet()) { | 
 | 1050 |     if (value->AsArrayGet()->GetArray() == instruction->GetArray()) { | 
 | 1051 |       // If the code is just swapping elements in the array, no need for a type check. | 
 | 1052 |       instruction->ClearNeedsTypeCheck(); | 
| Nicolas Geoffray | e0395dd | 2015-09-25 11:04:45 +0100 | [diff] [blame] | 1053 |       return; | 
| Nicolas Geoffray | af07bc1 | 2014-11-12 18:08:09 +0000 | [diff] [blame] | 1054 |     } | 
 | 1055 |   } | 
| Nicolas Geoffray | 07276db | 2015-05-18 14:22:09 +0100 | [diff] [blame] | 1056 |  | 
| Nicolas Geoffray | 9fdb31e | 2015-07-01 12:56:46 +0100 | [diff] [blame] | 1057 |   if (value->IsNullConstant()) { | 
 | 1058 |     instruction->ClearNeedsTypeCheck(); | 
| Nicolas Geoffray | e0395dd | 2015-09-25 11:04:45 +0100 | [diff] [blame] | 1059 |     return; | 
| Nicolas Geoffray | 9fdb31e | 2015-07-01 12:56:46 +0100 | [diff] [blame] | 1060 |   } | 
 | 1061 |  | 
| Nicolas Geoffray | e0395dd | 2015-09-25 11:04:45 +0100 | [diff] [blame] | 1062 |   ScopedObjectAccess soa(Thread::Current()); | 
 | 1063 |   ReferenceTypeInfo array_rti = instruction->GetArray()->GetReferenceTypeInfo(); | 
 | 1064 |   ReferenceTypeInfo value_rti = value->GetReferenceTypeInfo(); | 
 | 1065 |   if (!array_rti.IsValid()) { | 
 | 1066 |     return; | 
 | 1067 |   } | 
 | 1068 |  | 
 | 1069 |   if (value_rti.IsValid() && array_rti.CanArrayHold(value_rti)) { | 
 | 1070 |     instruction->ClearNeedsTypeCheck(); | 
 | 1071 |     return; | 
 | 1072 |   } | 
 | 1073 |  | 
 | 1074 |   if (array_rti.IsObjectArray()) { | 
 | 1075 |     if (array_rti.IsExact()) { | 
 | 1076 |       instruction->ClearNeedsTypeCheck(); | 
 | 1077 |       return; | 
 | 1078 |     } | 
 | 1079 |     instruction->SetStaticTypeOfArrayIsObjectArray(); | 
| Nicolas Geoffray | 07276db | 2015-05-18 14:22:09 +0100 | [diff] [blame] | 1080 |   } | 
| Nicolas Geoffray | af07bc1 | 2014-11-12 18:08:09 +0000 | [diff] [blame] | 1081 | } | 
 | 1082 |  | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 1083 | static bool IsTypeConversionLossless(DataType::Type input_type, DataType::Type result_type) { | 
| Aart Bik | dab6907 | 2017-10-23 13:30:39 -0700 | [diff] [blame] | 1084 |   // Make sure all implicit conversions have been simplified and no new ones have been introduced. | 
 | 1085 |   DCHECK(!DataType::IsTypeConversionImplicit(input_type, result_type)) | 
 | 1086 |       << input_type << "," << result_type; | 
| Vladimir Marko | b52bbde | 2016-02-12 12:06:05 +0000 | [diff] [blame] | 1087 |   // The conversion to a larger type is loss-less with the exception of two cases, | 
| Vladimir Marko | d5d2f2c | 2017-09-26 12:37:26 +0100 | [diff] [blame] | 1088 |   //   - conversion to the unsigned type Uint16, where we may lose some bits, and | 
| Vladimir Marko | b52bbde | 2016-02-12 12:06:05 +0000 | [diff] [blame] | 1089 |   //   - conversion from float to long, the only FP to integral conversion with smaller FP type. | 
 | 1090 |   // For integral to FP conversions this holds because the FP mantissa is large enough. | 
| Vladimir Marko | d5d2f2c | 2017-09-26 12:37:26 +0100 | [diff] [blame] | 1091 |   // Note: The size check excludes Uint8 as the result type. | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 1092 |   return DataType::Size(result_type) > DataType::Size(input_type) && | 
 | 1093 |       result_type != DataType::Type::kUint16 && | 
 | 1094 |       !(result_type == DataType::Type::kInt64 && input_type == DataType::Type::kFloat32); | 
| Vladimir Marko | b52bbde | 2016-02-12 12:06:05 +0000 | [diff] [blame] | 1095 | } | 
 | 1096 |  | 
| Vladimir Marko | 61b9228 | 2017-10-11 13:23:17 +0100 | [diff] [blame] | 1097 | static inline bool TryReplaceFieldOrArrayGetType(HInstruction* maybe_get, DataType::Type new_type) { | 
 | 1098 |   if (maybe_get->IsInstanceFieldGet()) { | 
 | 1099 |     maybe_get->AsInstanceFieldGet()->SetType(new_type); | 
 | 1100 |     return true; | 
 | 1101 |   } else if (maybe_get->IsStaticFieldGet()) { | 
 | 1102 |     maybe_get->AsStaticFieldGet()->SetType(new_type); | 
 | 1103 |     return true; | 
 | 1104 |   } else if (maybe_get->IsArrayGet() && !maybe_get->AsArrayGet()->IsStringCharAt()) { | 
 | 1105 |     maybe_get->AsArrayGet()->SetType(new_type); | 
 | 1106 |     return true; | 
 | 1107 |   } else { | 
 | 1108 |     return false; | 
 | 1109 |   } | 
 | 1110 | } | 
 | 1111 |  | 
| Mingyao Yang | 3bcb751 | 2017-11-16 15:40:46 -0800 | [diff] [blame] | 1112 | // The type conversion is only used for storing into a field/element of the | 
 | 1113 | // same/narrower size. | 
 | 1114 | static bool IsTypeConversionForStoringIntoNoWiderFieldOnly(HTypeConversion* type_conversion) { | 
 | 1115 |   if (type_conversion->HasEnvironmentUses()) { | 
 | 1116 |     return false; | 
 | 1117 |   } | 
 | 1118 |   DataType::Type input_type = type_conversion->GetInputType(); | 
 | 1119 |   DataType::Type result_type = type_conversion->GetResultType(); | 
 | 1120 |   if (!DataType::IsIntegralType(input_type) || | 
 | 1121 |       !DataType::IsIntegralType(result_type) || | 
 | 1122 |       input_type == DataType::Type::kInt64 || | 
 | 1123 |       result_type == DataType::Type::kInt64) { | 
 | 1124 |     // Type conversion is needed if non-integer types are involved, or 64-bit | 
 | 1125 |     // types are involved, which may use different number of registers. | 
 | 1126 |     return false; | 
 | 1127 |   } | 
 | 1128 |   if (DataType::Size(input_type) >= DataType::Size(result_type)) { | 
 | 1129 |     // Type conversion is not necessary when storing to a field/element of the | 
 | 1130 |     // same/smaller size. | 
 | 1131 |   } else { | 
 | 1132 |     // We do not handle this case here. | 
 | 1133 |     return false; | 
 | 1134 |   } | 
 | 1135 |  | 
 | 1136 |   // Check if the converted value is only used for storing into heap. | 
 | 1137 |   for (const HUseListNode<HInstruction*>& use : type_conversion->GetUses()) { | 
 | 1138 |     HInstruction* instruction = use.GetUser(); | 
 | 1139 |     if (instruction->IsInstanceFieldSet() && | 
 | 1140 |         instruction->AsInstanceFieldSet()->GetFieldType() == result_type) { | 
 | 1141 |       DCHECK_EQ(instruction->AsInstanceFieldSet()->GetValue(), type_conversion); | 
 | 1142 |       continue; | 
 | 1143 |     } | 
 | 1144 |     if (instruction->IsStaticFieldSet() && | 
 | 1145 |         instruction->AsStaticFieldSet()->GetFieldType() == result_type) { | 
 | 1146 |       DCHECK_EQ(instruction->AsStaticFieldSet()->GetValue(), type_conversion); | 
 | 1147 |       continue; | 
 | 1148 |     } | 
 | 1149 |     if (instruction->IsArraySet() && | 
 | 1150 |         instruction->AsArraySet()->GetComponentType() == result_type && | 
 | 1151 |         // not index use. | 
 | 1152 |         instruction->AsArraySet()->GetIndex() != type_conversion) { | 
 | 1153 |       DCHECK_EQ(instruction->AsArraySet()->GetValue(), type_conversion); | 
 | 1154 |       continue; | 
 | 1155 |     } | 
 | 1156 |     // The use is not as a store value, or the field/element type is not the | 
 | 1157 |     // same as the result_type, keep the type conversion. | 
 | 1158 |     return false; | 
 | 1159 |   } | 
 | 1160 |   // Codegen automatically handles the type conversion during the store. | 
 | 1161 |   return true; | 
 | 1162 | } | 
 | 1163 |  | 
| Nicolas Geoffray | 01fcc9e | 2014-12-01 14:16:20 +0000 | [diff] [blame] | 1164 | void InstructionSimplifierVisitor::VisitTypeConversion(HTypeConversion* instruction) { | 
| Vladimir Marko | b52bbde | 2016-02-12 12:06:05 +0000 | [diff] [blame] | 1165 |   HInstruction* input = instruction->GetInput(); | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 1166 |   DataType::Type input_type = input->GetType(); | 
 | 1167 |   DataType::Type result_type = instruction->GetResultType(); | 
| Nicolas Geoffray | acc56ac | 2018-10-09 08:45:24 +0100 | [diff] [blame] | 1168 |   if (instruction->IsImplicitConversion()) { | 
| Vladimir Marko | b52bbde | 2016-02-12 12:06:05 +0000 | [diff] [blame] | 1169 |     instruction->ReplaceWith(input); | 
| Nicolas Geoffray | 01fcc9e | 2014-12-01 14:16:20 +0000 | [diff] [blame] | 1170 |     instruction->GetBlock()->RemoveInstruction(instruction); | 
| Vladimir Marko | b52bbde | 2016-02-12 12:06:05 +0000 | [diff] [blame] | 1171 |     RecordSimplification(); | 
 | 1172 |     return; | 
 | 1173 |   } | 
 | 1174 |  | 
 | 1175 |   if (input->IsTypeConversion()) { | 
 | 1176 |     HTypeConversion* input_conversion = input->AsTypeConversion(); | 
 | 1177 |     HInstruction* original_input = input_conversion->GetInput(); | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 1178 |     DataType::Type original_type = original_input->GetType(); | 
| Vladimir Marko | b52bbde | 2016-02-12 12:06:05 +0000 | [diff] [blame] | 1179 |  | 
 | 1180 |     // When the first conversion is lossless, a direct conversion from the original type | 
 | 1181 |     // to the final type yields the same result, even for a lossy second conversion, for | 
 | 1182 |     // example float->double->int or int->double->float. | 
 | 1183 |     bool is_first_conversion_lossless = IsTypeConversionLossless(original_type, input_type); | 
 | 1184 |  | 
 | 1185 |     // For integral conversions, see if the first conversion loses only bits that the second | 
 | 1186 |     // doesn't need, i.e. the final type is no wider than the intermediate. If so, direct | 
 | 1187 |     // conversion yields the same result, for example long->int->short or int->char->short. | 
 | 1188 |     bool integral_conversions_with_non_widening_second = | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 1189 |         DataType::IsIntegralType(input_type) && | 
 | 1190 |         DataType::IsIntegralType(original_type) && | 
 | 1191 |         DataType::IsIntegralType(result_type) && | 
 | 1192 |         DataType::Size(result_type) <= DataType::Size(input_type); | 
| Vladimir Marko | b52bbde | 2016-02-12 12:06:05 +0000 | [diff] [blame] | 1193 |  | 
 | 1194 |     if (is_first_conversion_lossless || integral_conversions_with_non_widening_second) { | 
 | 1195 |       // If the merged conversion is implicit, do the simplification unconditionally. | 
| Vladimir Marko | d5d2f2c | 2017-09-26 12:37:26 +0100 | [diff] [blame] | 1196 |       if (DataType::IsTypeConversionImplicit(original_type, result_type)) { | 
| Vladimir Marko | b52bbde | 2016-02-12 12:06:05 +0000 | [diff] [blame] | 1197 |         instruction->ReplaceWith(original_input); | 
 | 1198 |         instruction->GetBlock()->RemoveInstruction(instruction); | 
 | 1199 |         if (!input_conversion->HasUses()) { | 
 | 1200 |           // Don't wait for DCE. | 
 | 1201 |           input_conversion->GetBlock()->RemoveInstruction(input_conversion); | 
 | 1202 |         } | 
 | 1203 |         RecordSimplification(); | 
 | 1204 |         return; | 
 | 1205 |       } | 
 | 1206 |       // Otherwise simplify only if the first conversion has no other use. | 
 | 1207 |       if (input_conversion->HasOnlyOneNonEnvironmentUse()) { | 
 | 1208 |         input_conversion->ReplaceWith(original_input); | 
 | 1209 |         input_conversion->GetBlock()->RemoveInstruction(input_conversion); | 
 | 1210 |         RecordSimplification(); | 
 | 1211 |         return; | 
 | 1212 |       } | 
 | 1213 |     } | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 1214 |   } else if (input->IsAnd() && DataType::IsIntegralType(result_type)) { | 
 | 1215 |     DCHECK(DataType::IsIntegralType(input_type)); | 
| Vladimir Marko | 8428bd3 | 2016-02-12 16:53:57 +0000 | [diff] [blame] | 1216 |     HAnd* input_and = input->AsAnd(); | 
 | 1217 |     HConstant* constant = input_and->GetConstantRight(); | 
 | 1218 |     if (constant != nullptr) { | 
 | 1219 |       int64_t value = Int64FromConstant(constant); | 
 | 1220 |       DCHECK_NE(value, -1);  // "& -1" would have been optimized away in VisitAnd(). | 
 | 1221 |       size_t trailing_ones = CTZ(~static_cast<uint64_t>(value)); | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 1222 |       if (trailing_ones >= kBitsPerByte * DataType::Size(result_type)) { | 
| Vladimir Marko | 8428bd3 | 2016-02-12 16:53:57 +0000 | [diff] [blame] | 1223 |         // The `HAnd` is useless, for example in `(byte) (x & 0xff)`, get rid of it. | 
| Vladimir Marko | 625090f | 2016-03-14 18:00:05 +0000 | [diff] [blame] | 1224 |         HInstruction* original_input = input_and->GetLeastConstantLeft(); | 
| Vladimir Marko | d5d2f2c | 2017-09-26 12:37:26 +0100 | [diff] [blame] | 1225 |         if (DataType::IsTypeConversionImplicit(original_input->GetType(), result_type)) { | 
| Vladimir Marko | 625090f | 2016-03-14 18:00:05 +0000 | [diff] [blame] | 1226 |           instruction->ReplaceWith(original_input); | 
 | 1227 |           instruction->GetBlock()->RemoveInstruction(instruction); | 
 | 1228 |           RecordSimplification(); | 
 | 1229 |           return; | 
 | 1230 |         } else if (input->HasOnlyOneNonEnvironmentUse()) { | 
 | 1231 |           input_and->ReplaceWith(original_input); | 
 | 1232 |           input_and->GetBlock()->RemoveInstruction(input_and); | 
 | 1233 |           RecordSimplification(); | 
 | 1234 |           return; | 
 | 1235 |         } | 
| Vladimir Marko | 8428bd3 | 2016-02-12 16:53:57 +0000 | [diff] [blame] | 1236 |       } | 
 | 1237 |     } | 
| Vladimir Marko | 61b9228 | 2017-10-11 13:23:17 +0100 | [diff] [blame] | 1238 |   } else if (input->HasOnlyOneNonEnvironmentUse() && | 
 | 1239 |              ((input_type == DataType::Type::kInt8 && result_type == DataType::Type::kUint8) || | 
 | 1240 |               (input_type == DataType::Type::kUint8 && result_type == DataType::Type::kInt8) || | 
 | 1241 |               (input_type == DataType::Type::kInt16 && result_type == DataType::Type::kUint16) || | 
 | 1242 |               (input_type == DataType::Type::kUint16 && result_type == DataType::Type::kInt16))) { | 
 | 1243 |     // Try to modify the type of the load to `result_type` and remove the explicit type conversion. | 
 | 1244 |     if (TryReplaceFieldOrArrayGetType(input, result_type)) { | 
 | 1245 |       instruction->ReplaceWith(input); | 
 | 1246 |       instruction->GetBlock()->RemoveInstruction(instruction); | 
 | 1247 |       RecordSimplification(); | 
 | 1248 |       return; | 
 | 1249 |     } | 
| Nicolas Geoffray | 01fcc9e | 2014-12-01 14:16:20 +0000 | [diff] [blame] | 1250 |   } | 
| Mingyao Yang | 3bcb751 | 2017-11-16 15:40:46 -0800 | [diff] [blame] | 1251 |  | 
 | 1252 |   if (IsTypeConversionForStoringIntoNoWiderFieldOnly(instruction)) { | 
 | 1253 |     instruction->ReplaceWith(input); | 
 | 1254 |     instruction->GetBlock()->RemoveInstruction(instruction); | 
 | 1255 |     RecordSimplification(); | 
 | 1256 |     return; | 
 | 1257 |   } | 
| Nicolas Geoffray | 01fcc9e | 2014-12-01 14:16:20 +0000 | [diff] [blame] | 1258 | } | 
 | 1259 |  | 
| Aart Bik | c6eec4b | 2018-03-29 17:22:00 -0700 | [diff] [blame] | 1260 | void InstructionSimplifierVisitor::VisitAbs(HAbs* instruction) { | 
 | 1261 |   HInstruction* input = instruction->GetInput(); | 
 | 1262 |   if (DataType::IsZeroExtension(input->GetType(), instruction->GetResultType())) { | 
 | 1263 |     // Zero extension from narrow to wide can never set sign bit in the wider | 
 | 1264 |     // operand, making the subsequent Abs redundant (e.g., abs(b & 0xff) for byte b). | 
 | 1265 |     instruction->ReplaceWith(input); | 
 | 1266 |     instruction->GetBlock()->RemoveInstruction(instruction); | 
 | 1267 |     RecordSimplification(); | 
 | 1268 |   } | 
 | 1269 | } | 
 | 1270 |  | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1271 | void InstructionSimplifierVisitor::VisitAdd(HAdd* instruction) { | 
 | 1272 |   HConstant* input_cst = instruction->GetConstantRight(); | 
 | 1273 |   HInstruction* input_other = instruction->GetLeastConstantLeft(); | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 1274 |   bool integral_type = DataType::IsIntegralType(instruction->GetType()); | 
| Roland Levillain | 1a65388 | 2016-03-18 18:05:57 +0000 | [diff] [blame] | 1275 |   if ((input_cst != nullptr) && input_cst->IsArithmeticZero()) { | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1276 |     // Replace code looking like | 
 | 1277 |     //    ADD dst, src, 0 | 
 | 1278 |     // with | 
 | 1279 |     //    src | 
| Serguei Katkov | 115b53f | 2015-08-05 17:03:30 +0600 | [diff] [blame] | 1280 |     // Note that we cannot optimize `x + 0.0` to `x` for floating-point. When | 
 | 1281 |     // `x` is `-0.0`, the former expression yields `0.0`, while the later | 
 | 1282 |     // yields `-0.0`. | 
| Maxim Kazantsev | d3278bd | 2016-07-12 15:55:33 +0600 | [diff] [blame] | 1283 |     if (integral_type) { | 
| Serguei Katkov | 115b53f | 2015-08-05 17:03:30 +0600 | [diff] [blame] | 1284 |       instruction->ReplaceWith(input_other); | 
 | 1285 |       instruction->GetBlock()->RemoveInstruction(instruction); | 
| Alexandre Rames | c5809c3 | 2016-05-25 15:01:06 +0100 | [diff] [blame] | 1286 |       RecordSimplification(); | 
| Serguei Katkov | 115b53f | 2015-08-05 17:03:30 +0600 | [diff] [blame] | 1287 |       return; | 
 | 1288 |     } | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 1289 |   } | 
 | 1290 |  | 
 | 1291 |   HInstruction* left = instruction->GetLeft(); | 
 | 1292 |   HInstruction* right = instruction->GetRight(); | 
 | 1293 |   bool left_is_neg = left->IsNeg(); | 
 | 1294 |   bool right_is_neg = right->IsNeg(); | 
 | 1295 |  | 
 | 1296 |   if (left_is_neg && right_is_neg) { | 
 | 1297 |     if (TryMoveNegOnInputsAfterBinop(instruction)) { | 
 | 1298 |       return; | 
 | 1299 |     } | 
 | 1300 |   } | 
 | 1301 |  | 
 | 1302 |   HNeg* neg = left_is_neg ? left->AsNeg() : right->AsNeg(); | 
| Andreas Gampe | 654698d | 2018-09-20 13:34:35 -0700 | [diff] [blame] | 1303 |   if (left_is_neg != right_is_neg && neg->HasOnlyOneNonEnvironmentUse()) { | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 1304 |     // Replace code looking like | 
 | 1305 |     //    NEG tmp, b | 
 | 1306 |     //    ADD dst, a, tmp | 
 | 1307 |     // with | 
 | 1308 |     //    SUB dst, a, b | 
 | 1309 |     // We do not perform the optimization if the input negation has environment | 
 | 1310 |     // uses or multiple non-environment uses as it could lead to worse code. In | 
 | 1311 |     // particular, we do not want the live range of `b` to be extended if we are | 
 | 1312 |     // not sure the initial 'NEG' instruction can be removed. | 
 | 1313 |     HInstruction* other = left_is_neg ? right : left; | 
| Vladimir Marko | ca6fff8 | 2017-10-03 14:49:14 +0100 | [diff] [blame] | 1314 |     HSub* sub = | 
 | 1315 |         new(GetGraph()->GetAllocator()) HSub(instruction->GetType(), other, neg->GetInput()); | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 1316 |     instruction->GetBlock()->ReplaceAndRemoveInstructionWith(instruction, sub); | 
 | 1317 |     RecordSimplification(); | 
 | 1318 |     neg->GetBlock()->RemoveInstruction(neg); | 
| Scott Wakeling | 40a04bf | 2015-12-11 09:50:36 +0000 | [diff] [blame] | 1319 |     return; | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1320 |   } | 
| Scott Wakeling | 40a04bf | 2015-12-11 09:50:36 +0000 | [diff] [blame] | 1321 |  | 
| Anton Kirilov | e14dc86 | 2016-05-13 17:56:15 +0100 | [diff] [blame] | 1322 |   if (TryReplaceWithRotate(instruction)) { | 
 | 1323 |     return; | 
 | 1324 |   } | 
 | 1325 |  | 
 | 1326 |   // TryHandleAssociativeAndCommutativeOperation() does not remove its input, | 
 | 1327 |   // so no need to return. | 
 | 1328 |   TryHandleAssociativeAndCommutativeOperation(instruction); | 
 | 1329 |  | 
| Maxim Kazantsev | d3278bd | 2016-07-12 15:55:33 +0600 | [diff] [blame] | 1330 |   if ((left->IsSub() || right->IsSub()) && | 
| Anton Kirilov | e14dc86 | 2016-05-13 17:56:15 +0100 | [diff] [blame] | 1331 |       TrySubtractionChainSimplification(instruction)) { | 
 | 1332 |     return; | 
 | 1333 |   } | 
| Maxim Kazantsev | d3278bd | 2016-07-12 15:55:33 +0600 | [diff] [blame] | 1334 |  | 
 | 1335 |   if (integral_type) { | 
 | 1336 |     // Replace code patterns looking like | 
 | 1337 |     //    SUB dst1, x, y        SUB dst1, x, y | 
 | 1338 |     //    ADD dst2, dst1, y     ADD dst2, y, dst1 | 
 | 1339 |     // with | 
 | 1340 |     //    SUB dst1, x, y | 
 | 1341 |     // ADD instruction is not needed in this case, we may use | 
 | 1342 |     // one of inputs of SUB instead. | 
 | 1343 |     if (left->IsSub() && left->InputAt(1) == right) { | 
 | 1344 |       instruction->ReplaceWith(left->InputAt(0)); | 
 | 1345 |       RecordSimplification(); | 
 | 1346 |       instruction->GetBlock()->RemoveInstruction(instruction); | 
 | 1347 |       return; | 
 | 1348 |     } else if (right->IsSub() && right->InputAt(1) == left) { | 
 | 1349 |       instruction->ReplaceWith(right->InputAt(0)); | 
 | 1350 |       RecordSimplification(); | 
 | 1351 |       instruction->GetBlock()->RemoveInstruction(instruction); | 
 | 1352 |       return; | 
 | 1353 |     } | 
 | 1354 |   } | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1355 | } | 
 | 1356 |  | 
 | 1357 | void InstructionSimplifierVisitor::VisitAnd(HAnd* instruction) { | 
| Vladimir Marko | 61b9228 | 2017-10-11 13:23:17 +0100 | [diff] [blame] | 1358 |   DCHECK(DataType::IsIntegralType(instruction->GetType())); | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1359 |   HConstant* input_cst = instruction->GetConstantRight(); | 
 | 1360 |   HInstruction* input_other = instruction->GetLeastConstantLeft(); | 
 | 1361 |  | 
| Vladimir Marko | 452c1b6 | 2015-09-25 14:44:17 +0100 | [diff] [blame] | 1362 |   if (input_cst != nullptr) { | 
 | 1363 |     int64_t value = Int64FromConstant(input_cst); | 
| Aart Bik | dab6907 | 2017-10-23 13:30:39 -0700 | [diff] [blame] | 1364 |     if (value == -1 || | 
 | 1365 |         // Similar cases under zero extension. | 
 | 1366 |         (DataType::IsUnsignedType(input_other->GetType()) && | 
 | 1367 |          ((DataType::MaxValueOfIntegralType(input_other->GetType()) & ~value) == 0))) { | 
| Vladimir Marko | 452c1b6 | 2015-09-25 14:44:17 +0100 | [diff] [blame] | 1368 |       // Replace code looking like | 
 | 1369 |       //    AND dst, src, 0xFFF...FF | 
 | 1370 |       // with | 
 | 1371 |       //    src | 
 | 1372 |       instruction->ReplaceWith(input_other); | 
 | 1373 |       instruction->GetBlock()->RemoveInstruction(instruction); | 
 | 1374 |       RecordSimplification(); | 
 | 1375 |       return; | 
 | 1376 |     } | 
| Vladimir Marko | c8fb211 | 2017-10-03 11:37:52 +0100 | [diff] [blame] | 1377 |     if (input_other->IsTypeConversion() && | 
 | 1378 |         input_other->GetType() == DataType::Type::kInt64 && | 
 | 1379 |         DataType::IsIntegralType(input_other->InputAt(0)->GetType()) && | 
 | 1380 |         IsInt<32>(value) && | 
 | 1381 |         input_other->HasOnlyOneNonEnvironmentUse()) { | 
 | 1382 |       // The AND can be reordered before the TypeConversion. Replace | 
 | 1383 |       //   LongConstant cst, <32-bit-constant-sign-extended-to-64-bits> | 
 | 1384 |       //   TypeConversion<Int64> tmp, src | 
 | 1385 |       //   AND dst, tmp, cst | 
 | 1386 |       // with | 
 | 1387 |       //   IntConstant cst, <32-bit-constant> | 
 | 1388 |       //   AND tmp, src, cst | 
 | 1389 |       //   TypeConversion<Int64> dst, tmp | 
 | 1390 |       // This helps 32-bit targets and does not hurt 64-bit targets. | 
 | 1391 |       // This also simplifies detection of other patterns, such as Uint8 loads. | 
 | 1392 |       HInstruction* new_and_input = input_other->InputAt(0); | 
 | 1393 |       // Implicit conversion Int64->Int64 would have been removed previously. | 
 | 1394 |       DCHECK_NE(new_and_input->GetType(), DataType::Type::kInt64); | 
 | 1395 |       HConstant* new_const = GetGraph()->GetConstant(DataType::Type::kInt32, value); | 
 | 1396 |       HAnd* new_and = | 
| Vladimir Marko | ca6fff8 | 2017-10-03 14:49:14 +0100 | [diff] [blame] | 1397 |           new (GetGraph()->GetAllocator()) HAnd(DataType::Type::kInt32, new_and_input, new_const); | 
| Vladimir Marko | c8fb211 | 2017-10-03 11:37:52 +0100 | [diff] [blame] | 1398 |       instruction->GetBlock()->InsertInstructionBefore(new_and, instruction); | 
 | 1399 |       HTypeConversion* new_conversion = | 
| Vladimir Marko | ca6fff8 | 2017-10-03 14:49:14 +0100 | [diff] [blame] | 1400 |           new (GetGraph()->GetAllocator()) HTypeConversion(DataType::Type::kInt64, new_and); | 
| Vladimir Marko | c8fb211 | 2017-10-03 11:37:52 +0100 | [diff] [blame] | 1401 |       instruction->GetBlock()->ReplaceAndRemoveInstructionWith(instruction, new_conversion); | 
 | 1402 |       input_other->GetBlock()->RemoveInstruction(input_other); | 
 | 1403 |       RecordSimplification(); | 
 | 1404 |       // Try to process the new And now, do not wait for the next round of simplifications. | 
 | 1405 |       instruction = new_and; | 
 | 1406 |       input_other = new_and_input; | 
 | 1407 |     } | 
| Vladimir Marko | 452c1b6 | 2015-09-25 14:44:17 +0100 | [diff] [blame] | 1408 |     // Eliminate And from UShr+And if the And-mask contains all the bits that | 
 | 1409 |     // can be non-zero after UShr. Transform Shr+And to UShr if the And-mask | 
 | 1410 |     // precisely clears the shifted-in sign bits. | 
 | 1411 |     if ((input_other->IsUShr() || input_other->IsShr()) && input_other->InputAt(1)->IsConstant()) { | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 1412 |       size_t reg_bits = (instruction->GetResultType() == DataType::Type::kInt64) ? 64 : 32; | 
| Vladimir Marko | 452c1b6 | 2015-09-25 14:44:17 +0100 | [diff] [blame] | 1413 |       size_t shift = Int64FromConstant(input_other->InputAt(1)->AsConstant()) & (reg_bits - 1); | 
 | 1414 |       size_t num_tail_bits_set = CTZ(value + 1); | 
 | 1415 |       if ((num_tail_bits_set >= reg_bits - shift) && input_other->IsUShr()) { | 
 | 1416 |         // This AND clears only bits known to be clear, for example "(x >>> 24) & 0xff". | 
 | 1417 |         instruction->ReplaceWith(input_other); | 
 | 1418 |         instruction->GetBlock()->RemoveInstruction(instruction); | 
 | 1419 |         RecordSimplification(); | 
 | 1420 |         return; | 
 | 1421 |       }  else if ((num_tail_bits_set == reg_bits - shift) && IsPowerOfTwo(value + 1) && | 
 | 1422 |           input_other->HasOnlyOneNonEnvironmentUse()) { | 
 | 1423 |         DCHECK(input_other->IsShr());  // For UShr, we would have taken the branch above. | 
 | 1424 |         // Replace SHR+AND with USHR, for example "(x >> 24) & 0xff" -> "x >>> 24". | 
| Vladimir Marko | ca6fff8 | 2017-10-03 14:49:14 +0100 | [diff] [blame] | 1425 |         HUShr* ushr = new (GetGraph()->GetAllocator()) HUShr(instruction->GetType(), | 
| Vladimir Marko | 69d310e | 2017-10-09 14:12:23 +0100 | [diff] [blame] | 1426 |                                                              input_other->InputAt(0), | 
 | 1427 |                                                              input_other->InputAt(1), | 
 | 1428 |                                                              input_other->GetDexPc()); | 
| Vladimir Marko | 452c1b6 | 2015-09-25 14:44:17 +0100 | [diff] [blame] | 1429 |         instruction->GetBlock()->ReplaceAndRemoveInstructionWith(instruction, ushr); | 
 | 1430 |         input_other->GetBlock()->RemoveInstruction(input_other); | 
 | 1431 |         RecordSimplification(); | 
 | 1432 |         return; | 
 | 1433 |       } | 
 | 1434 |     } | 
| Vladimir Marko | 61b9228 | 2017-10-11 13:23:17 +0100 | [diff] [blame] | 1435 |     if ((value == 0xff || value == 0xffff) && instruction->GetType() != DataType::Type::kInt64) { | 
 | 1436 |       // Transform AND to a type conversion to Uint8/Uint16. If `input_other` is a field | 
 | 1437 |       // or array Get with only a single use, short-circuit the subsequent simplification | 
 | 1438 |       // of the Get+TypeConversion and change the Get's type to `new_type` instead. | 
 | 1439 |       DataType::Type new_type = (value == 0xff) ? DataType::Type::kUint8 : DataType::Type::kUint16; | 
 | 1440 |       DataType::Type find_type = (value == 0xff) ? DataType::Type::kInt8 : DataType::Type::kInt16; | 
 | 1441 |       if (input_other->GetType() == find_type && | 
 | 1442 |           input_other->HasOnlyOneNonEnvironmentUse() && | 
 | 1443 |           TryReplaceFieldOrArrayGetType(input_other, new_type)) { | 
 | 1444 |         instruction->ReplaceWith(input_other); | 
 | 1445 |         instruction->GetBlock()->RemoveInstruction(instruction); | 
| Aart Bik | dab6907 | 2017-10-23 13:30:39 -0700 | [diff] [blame] | 1446 |       } else if (DataType::IsTypeConversionImplicit(input_other->GetType(), new_type)) { | 
 | 1447 |         instruction->ReplaceWith(input_other); | 
 | 1448 |         instruction->GetBlock()->RemoveInstruction(instruction); | 
| Vladimir Marko | 61b9228 | 2017-10-11 13:23:17 +0100 | [diff] [blame] | 1449 |       } else { | 
 | 1450 |         HTypeConversion* type_conversion = new (GetGraph()->GetAllocator()) HTypeConversion( | 
 | 1451 |             new_type, input_other, instruction->GetDexPc()); | 
 | 1452 |         instruction->GetBlock()->ReplaceAndRemoveInstructionWith(instruction, type_conversion); | 
 | 1453 |       } | 
 | 1454 |       RecordSimplification(); | 
 | 1455 |       return; | 
 | 1456 |     } | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1457 |   } | 
 | 1458 |  | 
 | 1459 |   // We assume that GVN has run before, so we only perform a pointer comparison. | 
 | 1460 |   // If for some reason the values are equal but the pointers are different, we | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 1461 |   // are still correct and only miss an optimization opportunity. | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1462 |   if (instruction->GetLeft() == instruction->GetRight()) { | 
 | 1463 |     // Replace code looking like | 
 | 1464 |     //    AND dst, src, src | 
 | 1465 |     // with | 
 | 1466 |     //    src | 
 | 1467 |     instruction->ReplaceWith(instruction->GetLeft()); | 
 | 1468 |     instruction->GetBlock()->RemoveInstruction(instruction); | 
| Alexandre Rames | c5809c3 | 2016-05-25 15:01:06 +0100 | [diff] [blame] | 1469 |     RecordSimplification(); | 
| Alexandre Rames | ca0e3a0 | 2016-02-03 10:54:07 +0000 | [diff] [blame] | 1470 |     return; | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1471 |   } | 
| Alexandre Rames | ca0e3a0 | 2016-02-03 10:54:07 +0000 | [diff] [blame] | 1472 |  | 
| Anton Kirilov | e14dc86 | 2016-05-13 17:56:15 +0100 | [diff] [blame] | 1473 |   if (TryDeMorganNegationFactoring(instruction)) { | 
 | 1474 |     return; | 
 | 1475 |   } | 
 | 1476 |  | 
 | 1477 |   // TryHandleAssociativeAndCommutativeOperation() does not remove its input, | 
 | 1478 |   // so no need to return. | 
 | 1479 |   TryHandleAssociativeAndCommutativeOperation(instruction); | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1480 | } | 
 | 1481 |  | 
| Mark Mendell | c470193 | 2015-04-10 13:18:51 -0400 | [diff] [blame] | 1482 | void InstructionSimplifierVisitor::VisitGreaterThan(HGreaterThan* condition) { | 
 | 1483 |   VisitCondition(condition); | 
 | 1484 | } | 
 | 1485 |  | 
 | 1486 | void InstructionSimplifierVisitor::VisitGreaterThanOrEqual(HGreaterThanOrEqual* condition) { | 
 | 1487 |   VisitCondition(condition); | 
 | 1488 | } | 
 | 1489 |  | 
 | 1490 | void InstructionSimplifierVisitor::VisitLessThan(HLessThan* condition) { | 
 | 1491 |   VisitCondition(condition); | 
 | 1492 | } | 
 | 1493 |  | 
 | 1494 | void InstructionSimplifierVisitor::VisitLessThanOrEqual(HLessThanOrEqual* condition) { | 
 | 1495 |   VisitCondition(condition); | 
 | 1496 | } | 
 | 1497 |  | 
| Anton Shamin | bdd7935 | 2016-02-15 12:48:36 +0600 | [diff] [blame] | 1498 | void InstructionSimplifierVisitor::VisitBelow(HBelow* condition) { | 
 | 1499 |   VisitCondition(condition); | 
 | 1500 | } | 
 | 1501 |  | 
 | 1502 | void InstructionSimplifierVisitor::VisitBelowOrEqual(HBelowOrEqual* condition) { | 
 | 1503 |   VisitCondition(condition); | 
 | 1504 | } | 
 | 1505 |  | 
 | 1506 | void InstructionSimplifierVisitor::VisitAbove(HAbove* condition) { | 
 | 1507 |   VisitCondition(condition); | 
 | 1508 | } | 
 | 1509 |  | 
 | 1510 | void InstructionSimplifierVisitor::VisitAboveOrEqual(HAboveOrEqual* condition) { | 
 | 1511 |   VisitCondition(condition); | 
 | 1512 | } | 
| Aart Bik | e9f3760 | 2015-10-09 11:15:55 -0700 | [diff] [blame] | 1513 |  | 
| Nicolas Geoffray | c52b26d | 2016-12-19 09:18:07 +0000 | [diff] [blame] | 1514 | // Recognize the following pattern: | 
 | 1515 | // obj.getClass() ==/!= Foo.class | 
 | 1516 | // And replace it with a constant value if the type of `obj` is statically known. | 
 | 1517 | static bool RecognizeAndSimplifyClassCheck(HCondition* condition) { | 
 | 1518 |   HInstruction* input_one = condition->InputAt(0); | 
 | 1519 |   HInstruction* input_two = condition->InputAt(1); | 
 | 1520 |   HLoadClass* load_class = input_one->IsLoadClass() | 
 | 1521 |       ? input_one->AsLoadClass() | 
 | 1522 |       : input_two->AsLoadClass(); | 
 | 1523 |   if (load_class == nullptr) { | 
 | 1524 |     return false; | 
 | 1525 |   } | 
 | 1526 |  | 
 | 1527 |   ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI(); | 
 | 1528 |   if (!class_rti.IsValid()) { | 
 | 1529 |     // Unresolved class. | 
 | 1530 |     return false; | 
 | 1531 |   } | 
 | 1532 |  | 
 | 1533 |   HInstanceFieldGet* field_get = (load_class == input_one) | 
 | 1534 |       ? input_two->AsInstanceFieldGet() | 
 | 1535 |       : input_one->AsInstanceFieldGet(); | 
 | 1536 |   if (field_get == nullptr) { | 
 | 1537 |     return false; | 
 | 1538 |   } | 
 | 1539 |  | 
 | 1540 |   HInstruction* receiver = field_get->InputAt(0); | 
 | 1541 |   ReferenceTypeInfo receiver_type = receiver->GetReferenceTypeInfo(); | 
 | 1542 |   if (!receiver_type.IsExact()) { | 
 | 1543 |     return false; | 
 | 1544 |   } | 
 | 1545 |  | 
 | 1546 |   { | 
 | 1547 |     ScopedObjectAccess soa(Thread::Current()); | 
| Vladimir Marko | b4eb1b1 | 2018-05-24 11:09:38 +0100 | [diff] [blame] | 1548 |     ArtField* field = GetClassRoot<mirror::Object>()->GetInstanceField(0); | 
| Nicolas Geoffray | c52b26d | 2016-12-19 09:18:07 +0000 | [diff] [blame] | 1549 |     DCHECK_EQ(std::string(field->GetName()), "shadow$_klass_"); | 
 | 1550 |     if (field_get->GetFieldInfo().GetField() != field) { | 
 | 1551 |       return false; | 
 | 1552 |     } | 
 | 1553 |  | 
 | 1554 |     // We can replace the compare. | 
 | 1555 |     int value = 0; | 
 | 1556 |     if (receiver_type.IsEqual(class_rti)) { | 
 | 1557 |       value = condition->IsEqual() ? 1 : 0; | 
 | 1558 |     } else { | 
 | 1559 |       value = condition->IsNotEqual() ? 1 : 0; | 
 | 1560 |     } | 
 | 1561 |     condition->ReplaceWith(condition->GetBlock()->GetGraph()->GetIntConstant(value)); | 
 | 1562 |     return true; | 
 | 1563 |   } | 
 | 1564 | } | 
 | 1565 |  | 
| Mark Mendell | c470193 | 2015-04-10 13:18:51 -0400 | [diff] [blame] | 1566 | void InstructionSimplifierVisitor::VisitCondition(HCondition* condition) { | 
| Nicolas Geoffray | c52b26d | 2016-12-19 09:18:07 +0000 | [diff] [blame] | 1567 |   if (condition->IsEqual() || condition->IsNotEqual()) { | 
 | 1568 |     if (RecognizeAndSimplifyClassCheck(condition)) { | 
 | 1569 |       return; | 
 | 1570 |     } | 
 | 1571 |   } | 
 | 1572 |  | 
| Anton Shamin | bdd7935 | 2016-02-15 12:48:36 +0600 | [diff] [blame] | 1573 |   // Reverse condition if left is constant. Our code generators prefer constant | 
 | 1574 |   // on the right hand side. | 
 | 1575 |   if (condition->GetLeft()->IsConstant() && !condition->GetRight()->IsConstant()) { | 
 | 1576 |     HBasicBlock* block = condition->GetBlock(); | 
| Vladimir Marko | ca6fff8 | 2017-10-03 14:49:14 +0100 | [diff] [blame] | 1577 |     HCondition* replacement = | 
 | 1578 |         GetOppositeConditionSwapOps(block->GetGraph()->GetAllocator(), condition); | 
| Anton Shamin | bdd7935 | 2016-02-15 12:48:36 +0600 | [diff] [blame] | 1579 |     // If it is a fp we must set the opposite bias. | 
 | 1580 |     if (replacement != nullptr) { | 
 | 1581 |       if (condition->IsLtBias()) { | 
 | 1582 |         replacement->SetBias(ComparisonBias::kGtBias); | 
 | 1583 |       } else if (condition->IsGtBias()) { | 
 | 1584 |         replacement->SetBias(ComparisonBias::kLtBias); | 
 | 1585 |       } | 
 | 1586 |       block->ReplaceAndRemoveInstructionWith(condition, replacement); | 
 | 1587 |       RecordSimplification(); | 
 | 1588 |  | 
 | 1589 |       condition = replacement; | 
 | 1590 |     } | 
 | 1591 |   } | 
| Mark Mendell | c470193 | 2015-04-10 13:18:51 -0400 | [diff] [blame] | 1592 |  | 
| Mark Mendell | c470193 | 2015-04-10 13:18:51 -0400 | [diff] [blame] | 1593 |   HInstruction* left = condition->GetLeft(); | 
 | 1594 |   HInstruction* right = condition->GetRight(); | 
| Anton Shamin | bdd7935 | 2016-02-15 12:48:36 +0600 | [diff] [blame] | 1595 |  | 
 | 1596 |   // Try to fold an HCompare into this HCondition. | 
 | 1597 |  | 
| Mark Mendell | c470193 | 2015-04-10 13:18:51 -0400 | [diff] [blame] | 1598 |   // We can only replace an HCondition which compares a Compare to 0. | 
 | 1599 |   // Both 'dx' and 'jack' generate a compare to 0 when compiling a | 
 | 1600 |   // condition with a long, float or double comparison as input. | 
 | 1601 |   if (!left->IsCompare() || !right->IsConstant() || right->AsIntConstant()->GetValue() != 0) { | 
 | 1602 |     // Conversion is not possible. | 
 | 1603 |     return; | 
 | 1604 |   } | 
 | 1605 |  | 
 | 1606 |   // Is the Compare only used for this purpose? | 
| Vladimir Marko | 46817b8 | 2016-03-29 12:21:58 +0100 | [diff] [blame] | 1607 |   if (!left->GetUses().HasExactlyOneElement()) { | 
| Mark Mendell | c470193 | 2015-04-10 13:18:51 -0400 | [diff] [blame] | 1608 |     // Someone else also wants the result of the compare. | 
 | 1609 |     return; | 
 | 1610 |   } | 
 | 1611 |  | 
| Vladimir Marko | 46817b8 | 2016-03-29 12:21:58 +0100 | [diff] [blame] | 1612 |   if (!left->GetEnvUses().empty()) { | 
| Mark Mendell | c470193 | 2015-04-10 13:18:51 -0400 | [diff] [blame] | 1613 |     // There is a reference to the compare result in an environment. Do we really need it? | 
 | 1614 |     if (GetGraph()->IsDebuggable()) { | 
 | 1615 |       return; | 
 | 1616 |     } | 
 | 1617 |  | 
 | 1618 |     // We have to ensure that there are no deopt points in the sequence. | 
 | 1619 |     if (left->HasAnyEnvironmentUseBefore(condition)) { | 
 | 1620 |       return; | 
 | 1621 |     } | 
 | 1622 |   } | 
 | 1623 |  | 
 | 1624 |   // Clean up any environment uses from the HCompare, if any. | 
 | 1625 |   left->RemoveEnvironmentUsers(); | 
 | 1626 |  | 
 | 1627 |   // We have decided to fold the HCompare into the HCondition. Transfer the information. | 
 | 1628 |   condition->SetBias(left->AsCompare()->GetBias()); | 
 | 1629 |  | 
 | 1630 |   // Replace the operands of the HCondition. | 
 | 1631 |   condition->ReplaceInput(left->InputAt(0), 0); | 
 | 1632 |   condition->ReplaceInput(left->InputAt(1), 1); | 
 | 1633 |  | 
 | 1634 |   // Remove the HCompare. | 
 | 1635 |   left->GetBlock()->RemoveInstruction(left); | 
 | 1636 |  | 
 | 1637 |   RecordSimplification(); | 
 | 1638 | } | 
 | 1639 |  | 
| Andreas Gampe | 9186ced | 2016-12-12 14:28:21 -0800 | [diff] [blame] | 1640 | // Return whether x / divisor == x * (1.0f / divisor), for every float x. | 
 | 1641 | static constexpr bool CanDivideByReciprocalMultiplyFloat(int32_t divisor) { | 
 | 1642 |   // True, if the most significant bits of divisor are 0. | 
 | 1643 |   return ((divisor & 0x7fffff) == 0); | 
 | 1644 | } | 
 | 1645 |  | 
 | 1646 | // Return whether x / divisor == x * (1.0 / divisor), for every double x. | 
 | 1647 | static constexpr bool CanDivideByReciprocalMultiplyDouble(int64_t divisor) { | 
 | 1648 |   // True, if the most significant bits of divisor are 0. | 
 | 1649 |   return ((divisor & ((UINT64_C(1) << 52) - 1)) == 0); | 
 | 1650 | } | 
 | 1651 |  | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1652 | void InstructionSimplifierVisitor::VisitDiv(HDiv* instruction) { | 
 | 1653 |   HConstant* input_cst = instruction->GetConstantRight(); | 
 | 1654 |   HInstruction* input_other = instruction->GetLeastConstantLeft(); | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 1655 |   DataType::Type type = instruction->GetType(); | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1656 |  | 
 | 1657 |   if ((input_cst != nullptr) && input_cst->IsOne()) { | 
 | 1658 |     // Replace code looking like | 
 | 1659 |     //    DIV dst, src, 1 | 
 | 1660 |     // with | 
 | 1661 |     //    src | 
 | 1662 |     instruction->ReplaceWith(input_other); | 
 | 1663 |     instruction->GetBlock()->RemoveInstruction(instruction); | 
| Alexandre Rames | c5809c3 | 2016-05-25 15:01:06 +0100 | [diff] [blame] | 1664 |     RecordSimplification(); | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1665 |     return; | 
 | 1666 |   } | 
 | 1667 |  | 
| Nicolas Geoffray | 0d22184 | 2015-04-27 08:53:46 +0000 | [diff] [blame] | 1668 |   if ((input_cst != nullptr) && input_cst->IsMinusOne()) { | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1669 |     // Replace code looking like | 
 | 1670 |     //    DIV dst, src, -1 | 
 | 1671 |     // with | 
 | 1672 |     //    NEG dst, src | 
 | 1673 |     instruction->GetBlock()->ReplaceAndRemoveInstructionWith( | 
| Vladimir Marko | ca6fff8 | 2017-10-03 14:49:14 +0100 | [diff] [blame] | 1674 |         instruction, new (GetGraph()->GetAllocator()) HNeg(type, input_other)); | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 1675 |     RecordSimplification(); | 
| Nicolas Geoffray | 0d22184 | 2015-04-27 08:53:46 +0000 | [diff] [blame] | 1676 |     return; | 
 | 1677 |   } | 
 | 1678 |  | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 1679 |   if ((input_cst != nullptr) && DataType::IsFloatingPointType(type)) { | 
| Nicolas Geoffray | 0d22184 | 2015-04-27 08:53:46 +0000 | [diff] [blame] | 1680 |     // Try replacing code looking like | 
 | 1681 |     //    DIV dst, src, constant | 
 | 1682 |     // with | 
 | 1683 |     //    MUL dst, src, 1 / constant | 
 | 1684 |     HConstant* reciprocal = nullptr; | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 1685 |     if (type == DataType::Type::kFloat64) { | 
| Nicolas Geoffray | 0d22184 | 2015-04-27 08:53:46 +0000 | [diff] [blame] | 1686 |       double value = input_cst->AsDoubleConstant()->GetValue(); | 
 | 1687 |       if (CanDivideByReciprocalMultiplyDouble(bit_cast<int64_t, double>(value))) { | 
 | 1688 |         reciprocal = GetGraph()->GetDoubleConstant(1.0 / value); | 
 | 1689 |       } | 
 | 1690 |     } else { | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 1691 |       DCHECK_EQ(type, DataType::Type::kFloat32); | 
| Nicolas Geoffray | 0d22184 | 2015-04-27 08:53:46 +0000 | [diff] [blame] | 1692 |       float value = input_cst->AsFloatConstant()->GetValue(); | 
 | 1693 |       if (CanDivideByReciprocalMultiplyFloat(bit_cast<int32_t, float>(value))) { | 
 | 1694 |         reciprocal = GetGraph()->GetFloatConstant(1.0f / value); | 
 | 1695 |       } | 
 | 1696 |     } | 
 | 1697 |  | 
 | 1698 |     if (reciprocal != nullptr) { | 
 | 1699 |       instruction->GetBlock()->ReplaceAndRemoveInstructionWith( | 
| Vladimir Marko | ca6fff8 | 2017-10-03 14:49:14 +0100 | [diff] [blame] | 1700 |           instruction, new (GetGraph()->GetAllocator()) HMul(type, input_other, reciprocal)); | 
| Nicolas Geoffray | 0d22184 | 2015-04-27 08:53:46 +0000 | [diff] [blame] | 1701 |       RecordSimplification(); | 
 | 1702 |       return; | 
 | 1703 |     } | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1704 |   } | 
 | 1705 | } | 
 | 1706 |  | 
| Evgeny Astigeevich | 6587d91 | 2020-06-12 10:51:43 +0100 | [diff] [blame] | 1707 |  | 
 | 1708 | // Search HDiv having the specified dividend and divisor which is in the specified basic block. | 
 | 1709 | // Return nullptr if nothing has been found. | 
 | 1710 | static HInstruction* FindDivWithInputsInBasicBlock(HInstruction* dividend, | 
 | 1711 |                                                    HInstruction* divisor, | 
 | 1712 |                                                    HBasicBlock* basic_block) { | 
 | 1713 |   for (const HUseListNode<HInstruction*>& use : dividend->GetUses()) { | 
 | 1714 |     HInstruction* user = use.GetUser(); | 
 | 1715 |     if (user->GetBlock() == basic_block && user->IsDiv() && user->InputAt(1) == divisor) { | 
 | 1716 |       return user; | 
 | 1717 |     } | 
 | 1718 |   } | 
 | 1719 |   return nullptr; | 
 | 1720 | } | 
 | 1721 |  | 
 | 1722 | // If there is Div with the same inputs as Rem and in the same basic block, it can be reused. | 
 | 1723 | // Rem is replaced with Mul+Sub which use the found Div. | 
 | 1724 | void InstructionSimplifierVisitor::TryToReuseDiv(HRem* rem) { | 
 | 1725 |   // As the optimization replaces Rem with Mul+Sub they prevent some loop optimizations | 
 | 1726 |   // if the Rem is in a loop. | 
 | 1727 |   // Check if it is allowed to optimize such Rems. | 
 | 1728 |   if (rem->IsInLoop() && be_loop_friendly_) { | 
 | 1729 |     return; | 
 | 1730 |   } | 
 | 1731 |   DataType::Type type = rem->GetResultType(); | 
 | 1732 |   if (!DataType::IsIntOrLongType(type)) { | 
 | 1733 |     return; | 
 | 1734 |   } | 
 | 1735 |  | 
 | 1736 |   HBasicBlock* basic_block = rem->GetBlock(); | 
 | 1737 |   HInstruction* dividend = rem->GetLeft(); | 
 | 1738 |   HInstruction* divisor = rem->GetRight(); | 
 | 1739 |  | 
 | 1740 |   if (divisor->IsConstant()) { | 
 | 1741 |     HConstant* input_cst = rem->GetConstantRight(); | 
 | 1742 |     DCHECK(input_cst->IsIntConstant() || input_cst->IsLongConstant()); | 
 | 1743 |     int64_t cst_value = Int64FromConstant(input_cst); | 
 | 1744 |     if (cst_value == std::numeric_limits<int64_t>::min() || IsPowerOfTwo(std::abs(cst_value))) { | 
 | 1745 |       // Such cases are usually handled in the code generator because they don't need Div at all. | 
 | 1746 |       return; | 
 | 1747 |     } | 
 | 1748 |   } | 
 | 1749 |  | 
 | 1750 |   HInstruction* quotient = FindDivWithInputsInBasicBlock(dividend, divisor, basic_block); | 
 | 1751 |   if (quotient == nullptr) { | 
 | 1752 |     return; | 
 | 1753 |   } | 
 | 1754 |   if (!quotient->StrictlyDominates(rem)) { | 
 | 1755 |     quotient->MoveBefore(rem); | 
 | 1756 |   } | 
 | 1757 |  | 
 | 1758 |   ArenaAllocator* allocator = GetGraph()->GetAllocator(); | 
 | 1759 |   HInstruction* mul = new (allocator) HMul(type, quotient, divisor); | 
 | 1760 |   basic_block->InsertInstructionBefore(mul, rem); | 
 | 1761 |   HInstruction* sub = new (allocator) HSub(type, dividend, mul); | 
 | 1762 |   basic_block->InsertInstructionBefore(sub, rem); | 
 | 1763 |   rem->ReplaceWith(sub); | 
 | 1764 |   basic_block->RemoveInstruction(rem); | 
 | 1765 |   RecordSimplification(); | 
 | 1766 | } | 
 | 1767 |  | 
 | 1768 | void InstructionSimplifierVisitor::VisitRem(HRem* rem) { | 
 | 1769 |   TryToReuseDiv(rem); | 
 | 1770 | } | 
 | 1771 |  | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1772 | void InstructionSimplifierVisitor::VisitMul(HMul* instruction) { | 
 | 1773 |   HConstant* input_cst = instruction->GetConstantRight(); | 
 | 1774 |   HInstruction* input_other = instruction->GetLeastConstantLeft(); | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 1775 |   DataType::Type type = instruction->GetType(); | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1776 |   HBasicBlock* block = instruction->GetBlock(); | 
| Vladimir Marko | ca6fff8 | 2017-10-03 14:49:14 +0100 | [diff] [blame] | 1777 |   ArenaAllocator* allocator = GetGraph()->GetAllocator(); | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1778 |  | 
 | 1779 |   if (input_cst == nullptr) { | 
 | 1780 |     return; | 
 | 1781 |   } | 
 | 1782 |  | 
 | 1783 |   if (input_cst->IsOne()) { | 
 | 1784 |     // Replace code looking like | 
 | 1785 |     //    MUL dst, src, 1 | 
 | 1786 |     // with | 
 | 1787 |     //    src | 
 | 1788 |     instruction->ReplaceWith(input_other); | 
 | 1789 |     instruction->GetBlock()->RemoveInstruction(instruction); | 
| Alexandre Rames | c5809c3 | 2016-05-25 15:01:06 +0100 | [diff] [blame] | 1790 |     RecordSimplification(); | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1791 |     return; | 
 | 1792 |   } | 
 | 1793 |  | 
 | 1794 |   if (input_cst->IsMinusOne() && | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 1795 |       (DataType::IsFloatingPointType(type) || DataType::IsIntOrLongType(type))) { | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1796 |     // Replace code looking like | 
 | 1797 |     //    MUL dst, src, -1 | 
 | 1798 |     // with | 
 | 1799 |     //    NEG dst, src | 
 | 1800 |     HNeg* neg = new (allocator) HNeg(type, input_other); | 
 | 1801 |     block->ReplaceAndRemoveInstructionWith(instruction, neg); | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 1802 |     RecordSimplification(); | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1803 |     return; | 
 | 1804 |   } | 
 | 1805 |  | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 1806 |   if (DataType::IsFloatingPointType(type) && | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1807 |       ((input_cst->IsFloatConstant() && input_cst->AsFloatConstant()->GetValue() == 2.0f) || | 
 | 1808 |        (input_cst->IsDoubleConstant() && input_cst->AsDoubleConstant()->GetValue() == 2.0))) { | 
 | 1809 |     // Replace code looking like | 
 | 1810 |     //    FP_MUL dst, src, 2.0 | 
 | 1811 |     // with | 
 | 1812 |     //    FP_ADD dst, src, src | 
 | 1813 |     // The 'int' and 'long' cases are handled below. | 
 | 1814 |     block->ReplaceAndRemoveInstructionWith(instruction, | 
 | 1815 |                                            new (allocator) HAdd(type, input_other, input_other)); | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 1816 |     RecordSimplification(); | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1817 |     return; | 
 | 1818 |   } | 
 | 1819 |  | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 1820 |   if (DataType::IsIntOrLongType(type)) { | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1821 |     int64_t factor = Int64FromConstant(input_cst); | 
| Serguei Katkov | 5384919 | 2015-04-20 14:22:27 +0600 | [diff] [blame] | 1822 |     // Even though constant propagation also takes care of the zero case, other | 
 | 1823 |     // optimizations can lead to having a zero multiplication. | 
 | 1824 |     if (factor == 0) { | 
 | 1825 |       // Replace code looking like | 
 | 1826 |       //    MUL dst, src, 0 | 
 | 1827 |       // with | 
 | 1828 |       //    0 | 
 | 1829 |       instruction->ReplaceWith(input_cst); | 
 | 1830 |       instruction->GetBlock()->RemoveInstruction(instruction); | 
| Alexandre Rames | c5809c3 | 2016-05-25 15:01:06 +0100 | [diff] [blame] | 1831 |       RecordSimplification(); | 
| Anton Kirilov | e14dc86 | 2016-05-13 17:56:15 +0100 | [diff] [blame] | 1832 |       return; | 
| Serguei Katkov | 5384919 | 2015-04-20 14:22:27 +0600 | [diff] [blame] | 1833 |     } else if (IsPowerOfTwo(factor)) { | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1834 |       // Replace code looking like | 
 | 1835 |       //    MUL dst, src, pow_of_2 | 
 | 1836 |       // with | 
 | 1837 |       //    SHL dst, src, log2(pow_of_2) | 
| David Brazdil | 8d5b8b2 | 2015-03-24 10:51:52 +0000 | [diff] [blame] | 1838 |       HIntConstant* shift = GetGraph()->GetIntConstant(WhichPowerOf2(factor)); | 
| Roland Levillain | 22c4922 | 2016-03-18 14:04:28 +0000 | [diff] [blame] | 1839 |       HShl* shl = new (allocator) HShl(type, input_other, shift); | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1840 |       block->ReplaceAndRemoveInstructionWith(instruction, shl); | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 1841 |       RecordSimplification(); | 
| Anton Kirilov | e14dc86 | 2016-05-13 17:56:15 +0100 | [diff] [blame] | 1842 |       return; | 
| Alexandre Rames | 38db785 | 2015-11-20 15:02:45 +0000 | [diff] [blame] | 1843 |     } else if (IsPowerOfTwo(factor - 1)) { | 
 | 1844 |       // Transform code looking like | 
 | 1845 |       //    MUL dst, src, (2^n + 1) | 
 | 1846 |       // into | 
 | 1847 |       //    SHL tmp, src, n | 
 | 1848 |       //    ADD dst, src, tmp | 
 | 1849 |       HShl* shl = new (allocator) HShl(type, | 
 | 1850 |                                        input_other, | 
 | 1851 |                                        GetGraph()->GetIntConstant(WhichPowerOf2(factor - 1))); | 
 | 1852 |       HAdd* add = new (allocator) HAdd(type, input_other, shl); | 
 | 1853 |  | 
 | 1854 |       block->InsertInstructionBefore(shl, instruction); | 
 | 1855 |       block->ReplaceAndRemoveInstructionWith(instruction, add); | 
 | 1856 |       RecordSimplification(); | 
| Anton Kirilov | e14dc86 | 2016-05-13 17:56:15 +0100 | [diff] [blame] | 1857 |       return; | 
| Alexandre Rames | 38db785 | 2015-11-20 15:02:45 +0000 | [diff] [blame] | 1858 |     } else if (IsPowerOfTwo(factor + 1)) { | 
 | 1859 |       // Transform code looking like | 
 | 1860 |       //    MUL dst, src, (2^n - 1) | 
 | 1861 |       // into | 
 | 1862 |       //    SHL tmp, src, n | 
 | 1863 |       //    SUB dst, tmp, src | 
 | 1864 |       HShl* shl = new (allocator) HShl(type, | 
 | 1865 |                                        input_other, | 
 | 1866 |                                        GetGraph()->GetIntConstant(WhichPowerOf2(factor + 1))); | 
 | 1867 |       HSub* sub = new (allocator) HSub(type, shl, input_other); | 
 | 1868 |  | 
 | 1869 |       block->InsertInstructionBefore(shl, instruction); | 
 | 1870 |       block->ReplaceAndRemoveInstructionWith(instruction, sub); | 
 | 1871 |       RecordSimplification(); | 
| Anton Kirilov | e14dc86 | 2016-05-13 17:56:15 +0100 | [diff] [blame] | 1872 |       return; | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1873 |     } | 
 | 1874 |   } | 
| Anton Kirilov | e14dc86 | 2016-05-13 17:56:15 +0100 | [diff] [blame] | 1875 |  | 
 | 1876 |   // TryHandleAssociativeAndCommutativeOperation() does not remove its input, | 
 | 1877 |   // so no need to return. | 
 | 1878 |   TryHandleAssociativeAndCommutativeOperation(instruction); | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1879 | } | 
 | 1880 |  | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 1881 | void InstructionSimplifierVisitor::VisitNeg(HNeg* instruction) { | 
 | 1882 |   HInstruction* input = instruction->GetInput(); | 
 | 1883 |   if (input->IsNeg()) { | 
 | 1884 |     // Replace code looking like | 
 | 1885 |     //    NEG tmp, src | 
 | 1886 |     //    NEG dst, tmp | 
 | 1887 |     // with | 
 | 1888 |     //    src | 
 | 1889 |     HNeg* previous_neg = input->AsNeg(); | 
 | 1890 |     instruction->ReplaceWith(previous_neg->GetInput()); | 
 | 1891 |     instruction->GetBlock()->RemoveInstruction(instruction); | 
 | 1892 |     // We perform the optimization even if the input negation has environment | 
 | 1893 |     // uses since it allows removing the current instruction. But we only delete | 
 | 1894 |     // the input negation only if it is does not have any uses left. | 
 | 1895 |     if (!previous_neg->HasUses()) { | 
 | 1896 |       previous_neg->GetBlock()->RemoveInstruction(previous_neg); | 
 | 1897 |     } | 
 | 1898 |     RecordSimplification(); | 
 | 1899 |     return; | 
 | 1900 |   } | 
 | 1901 |  | 
| Serguei Katkov | 339dfc2 | 2015-04-20 12:29:32 +0600 | [diff] [blame] | 1902 |   if (input->IsSub() && input->HasOnlyOneNonEnvironmentUse() && | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 1903 |       !DataType::IsFloatingPointType(input->GetType())) { | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 1904 |     // Replace code looking like | 
 | 1905 |     //    SUB tmp, a, b | 
 | 1906 |     //    NEG dst, tmp | 
 | 1907 |     // with | 
 | 1908 |     //    SUB dst, b, a | 
 | 1909 |     // We do not perform the optimization if the input subtraction has | 
 | 1910 |     // environment uses or multiple non-environment uses as it could lead to | 
 | 1911 |     // worse code. In particular, we do not want the live ranges of `a` and `b` | 
 | 1912 |     // to be extended if we are not sure the initial 'SUB' instruction can be | 
 | 1913 |     // removed. | 
| Serguei Katkov | 339dfc2 | 2015-04-20 12:29:32 +0600 | [diff] [blame] | 1914 |     // We do not perform optimization for fp because we could lose the sign of zero. | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 1915 |     HSub* sub = input->AsSub(); | 
| Vladimir Marko | ca6fff8 | 2017-10-03 14:49:14 +0100 | [diff] [blame] | 1916 |     HSub* new_sub = new (GetGraph()->GetAllocator()) HSub( | 
 | 1917 |         instruction->GetType(), sub->GetRight(), sub->GetLeft()); | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 1918 |     instruction->GetBlock()->ReplaceAndRemoveInstructionWith(instruction, new_sub); | 
 | 1919 |     if (!sub->HasUses()) { | 
 | 1920 |       sub->GetBlock()->RemoveInstruction(sub); | 
 | 1921 |     } | 
 | 1922 |     RecordSimplification(); | 
 | 1923 |   } | 
 | 1924 | } | 
 | 1925 |  | 
 | 1926 | void InstructionSimplifierVisitor::VisitNot(HNot* instruction) { | 
 | 1927 |   HInstruction* input = instruction->GetInput(); | 
 | 1928 |   if (input->IsNot()) { | 
 | 1929 |     // Replace code looking like | 
 | 1930 |     //    NOT tmp, src | 
 | 1931 |     //    NOT dst, tmp | 
 | 1932 |     // with | 
 | 1933 |     //    src | 
 | 1934 |     // We perform the optimization even if the input negation has environment | 
 | 1935 |     // uses since it allows removing the current instruction. But we only delete | 
 | 1936 |     // the input negation only if it is does not have any uses left. | 
 | 1937 |     HNot* previous_not = input->AsNot(); | 
 | 1938 |     instruction->ReplaceWith(previous_not->GetInput()); | 
 | 1939 |     instruction->GetBlock()->RemoveInstruction(instruction); | 
 | 1940 |     if (!previous_not->HasUses()) { | 
 | 1941 |       previous_not->GetBlock()->RemoveInstruction(previous_not); | 
 | 1942 |     } | 
 | 1943 |     RecordSimplification(); | 
 | 1944 |   } | 
 | 1945 | } | 
 | 1946 |  | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1947 | void InstructionSimplifierVisitor::VisitOr(HOr* instruction) { | 
 | 1948 |   HConstant* input_cst = instruction->GetConstantRight(); | 
 | 1949 |   HInstruction* input_other = instruction->GetLeastConstantLeft(); | 
 | 1950 |  | 
| Roland Levillain | 1a65388 | 2016-03-18 18:05:57 +0000 | [diff] [blame] | 1951 |   if ((input_cst != nullptr) && input_cst->IsZeroBitPattern()) { | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1952 |     // Replace code looking like | 
 | 1953 |     //    OR dst, src, 0 | 
 | 1954 |     // with | 
 | 1955 |     //    src | 
 | 1956 |     instruction->ReplaceWith(input_other); | 
 | 1957 |     instruction->GetBlock()->RemoveInstruction(instruction); | 
| Alexandre Rames | c5809c3 | 2016-05-25 15:01:06 +0100 | [diff] [blame] | 1958 |     RecordSimplification(); | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1959 |     return; | 
 | 1960 |   } | 
 | 1961 |  | 
 | 1962 |   // We assume that GVN has run before, so we only perform a pointer comparison. | 
 | 1963 |   // If for some reason the values are equal but the pointers are different, we | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 1964 |   // are still correct and only miss an optimization opportunity. | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1965 |   if (instruction->GetLeft() == instruction->GetRight()) { | 
 | 1966 |     // Replace code looking like | 
 | 1967 |     //    OR dst, src, src | 
 | 1968 |     // with | 
 | 1969 |     //    src | 
 | 1970 |     instruction->ReplaceWith(instruction->GetLeft()); | 
 | 1971 |     instruction->GetBlock()->RemoveInstruction(instruction); | 
| Alexandre Rames | c5809c3 | 2016-05-25 15:01:06 +0100 | [diff] [blame] | 1972 |     RecordSimplification(); | 
| Scott Wakeling | 40a04bf | 2015-12-11 09:50:36 +0000 | [diff] [blame] | 1973 |     return; | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1974 |   } | 
| Scott Wakeling | 40a04bf | 2015-12-11 09:50:36 +0000 | [diff] [blame] | 1975 |  | 
| Alexandre Rames | ca0e3a0 | 2016-02-03 10:54:07 +0000 | [diff] [blame] | 1976 |   if (TryDeMorganNegationFactoring(instruction)) return; | 
 | 1977 |  | 
| Anton Kirilov | e14dc86 | 2016-05-13 17:56:15 +0100 | [diff] [blame] | 1978 |   if (TryReplaceWithRotate(instruction)) { | 
 | 1979 |     return; | 
 | 1980 |   } | 
 | 1981 |  | 
 | 1982 |   // TryHandleAssociativeAndCommutativeOperation() does not remove its input, | 
 | 1983 |   // so no need to return. | 
 | 1984 |   TryHandleAssociativeAndCommutativeOperation(instruction); | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 1985 | } | 
 | 1986 |  | 
 | 1987 | void InstructionSimplifierVisitor::VisitShl(HShl* instruction) { | 
 | 1988 |   VisitShift(instruction); | 
 | 1989 | } | 
 | 1990 |  | 
 | 1991 | void InstructionSimplifierVisitor::VisitShr(HShr* instruction) { | 
 | 1992 |   VisitShift(instruction); | 
 | 1993 | } | 
 | 1994 |  | 
 | 1995 | void InstructionSimplifierVisitor::VisitSub(HSub* instruction) { | 
 | 1996 |   HConstant* input_cst = instruction->GetConstantRight(); | 
 | 1997 |   HInstruction* input_other = instruction->GetLeastConstantLeft(); | 
 | 1998 |  | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 1999 |   DataType::Type type = instruction->GetType(); | 
 | 2000 |   if (DataType::IsFloatingPointType(type)) { | 
| Serguei Katkov | 115b53f | 2015-08-05 17:03:30 +0600 | [diff] [blame] | 2001 |     return; | 
 | 2002 |   } | 
 | 2003 |  | 
| Roland Levillain | 1a65388 | 2016-03-18 18:05:57 +0000 | [diff] [blame] | 2004 |   if ((input_cst != nullptr) && input_cst->IsArithmeticZero()) { | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 2005 |     // Replace code looking like | 
 | 2006 |     //    SUB dst, src, 0 | 
 | 2007 |     // with | 
 | 2008 |     //    src | 
| Serguei Katkov | 115b53f | 2015-08-05 17:03:30 +0600 | [diff] [blame] | 2009 |     // Note that we cannot optimize `x - 0.0` to `x` for floating-point. When | 
 | 2010 |     // `x` is `-0.0`, the former expression yields `0.0`, while the later | 
 | 2011 |     // yields `-0.0`. | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 2012 |     instruction->ReplaceWith(input_other); | 
 | 2013 |     instruction->GetBlock()->RemoveInstruction(instruction); | 
| Alexandre Rames | c5809c3 | 2016-05-25 15:01:06 +0100 | [diff] [blame] | 2014 |     RecordSimplification(); | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 2015 |     return; | 
 | 2016 |   } | 
 | 2017 |  | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 2018 |   HBasicBlock* block = instruction->GetBlock(); | 
| Vladimir Marko | ca6fff8 | 2017-10-03 14:49:14 +0100 | [diff] [blame] | 2019 |   ArenaAllocator* allocator = GetGraph()->GetAllocator(); | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 2020 |  | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 2021 |   HInstruction* left = instruction->GetLeft(); | 
 | 2022 |   HInstruction* right = instruction->GetRight(); | 
 | 2023 |   if (left->IsConstant()) { | 
 | 2024 |     if (Int64FromConstant(left->AsConstant()) == 0) { | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 2025 |       // Replace code looking like | 
 | 2026 |       //    SUB dst, 0, src | 
 | 2027 |       // with | 
 | 2028 |       //    NEG dst, src | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 2029 |       // Note that we cannot optimize `0.0 - x` to `-x` for floating-point. When | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 2030 |       // `x` is `0.0`, the former expression yields `0.0`, while the later | 
 | 2031 |       // yields `-0.0`. | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 2032 |       HNeg* neg = new (allocator) HNeg(type, right); | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 2033 |       block->ReplaceAndRemoveInstructionWith(instruction, neg); | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 2034 |       RecordSimplification(); | 
 | 2035 |       return; | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 2036 |     } | 
 | 2037 |   } | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 2038 |  | 
 | 2039 |   if (left->IsNeg() && right->IsNeg()) { | 
 | 2040 |     if (TryMoveNegOnInputsAfterBinop(instruction)) { | 
 | 2041 |       return; | 
 | 2042 |     } | 
 | 2043 |   } | 
 | 2044 |  | 
 | 2045 |   if (right->IsNeg() && right->HasOnlyOneNonEnvironmentUse()) { | 
 | 2046 |     // Replace code looking like | 
 | 2047 |     //    NEG tmp, b | 
 | 2048 |     //    SUB dst, a, tmp | 
 | 2049 |     // with | 
 | 2050 |     //    ADD dst, a, b | 
| Vladimir Marko | ca6fff8 | 2017-10-03 14:49:14 +0100 | [diff] [blame] | 2051 |     HAdd* add = new(GetGraph()->GetAllocator()) HAdd(type, left, right->AsNeg()->GetInput()); | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 2052 |     instruction->GetBlock()->ReplaceAndRemoveInstructionWith(instruction, add); | 
 | 2053 |     RecordSimplification(); | 
 | 2054 |     right->GetBlock()->RemoveInstruction(right); | 
 | 2055 |     return; | 
 | 2056 |   } | 
 | 2057 |  | 
 | 2058 |   if (left->IsNeg() && left->HasOnlyOneNonEnvironmentUse()) { | 
 | 2059 |     // Replace code looking like | 
 | 2060 |     //    NEG tmp, a | 
 | 2061 |     //    SUB dst, tmp, b | 
 | 2062 |     // with | 
 | 2063 |     //    ADD tmp, a, b | 
 | 2064 |     //    NEG dst, tmp | 
 | 2065 |     // The second version is not intrinsically better, but enables more | 
 | 2066 |     // transformations. | 
| Vladimir Marko | ca6fff8 | 2017-10-03 14:49:14 +0100 | [diff] [blame] | 2067 |     HAdd* add = new(GetGraph()->GetAllocator()) HAdd(type, left->AsNeg()->GetInput(), right); | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 2068 |     instruction->GetBlock()->InsertInstructionBefore(add, instruction); | 
| Vladimir Marko | ca6fff8 | 2017-10-03 14:49:14 +0100 | [diff] [blame] | 2069 |     HNeg* neg = new (GetGraph()->GetAllocator()) HNeg(instruction->GetType(), add); | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 2070 |     instruction->GetBlock()->InsertInstructionBefore(neg, instruction); | 
 | 2071 |     instruction->ReplaceWith(neg); | 
 | 2072 |     instruction->GetBlock()->RemoveInstruction(instruction); | 
 | 2073 |     RecordSimplification(); | 
 | 2074 |     left->GetBlock()->RemoveInstruction(left); | 
| Anton Kirilov | e14dc86 | 2016-05-13 17:56:15 +0100 | [diff] [blame] | 2075 |     return; | 
 | 2076 |   } | 
 | 2077 |  | 
 | 2078 |   if (TrySubtractionChainSimplification(instruction)) { | 
 | 2079 |     return; | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 2080 |   } | 
| Maxim Kazantsev | d3278bd | 2016-07-12 15:55:33 +0600 | [diff] [blame] | 2081 |  | 
 | 2082 |   if (left->IsAdd()) { | 
 | 2083 |     // Replace code patterns looking like | 
 | 2084 |     //    ADD dst1, x, y        ADD dst1, x, y | 
 | 2085 |     //    SUB dst2, dst1, y     SUB dst2, dst1, x | 
 | 2086 |     // with | 
 | 2087 |     //    ADD dst1, x, y | 
 | 2088 |     // SUB instruction is not needed in this case, we may use | 
 | 2089 |     // one of inputs of ADD instead. | 
 | 2090 |     // It is applicable to integral types only. | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 2091 |     DCHECK(DataType::IsIntegralType(type)); | 
| Maxim Kazantsev | d3278bd | 2016-07-12 15:55:33 +0600 | [diff] [blame] | 2092 |     if (left->InputAt(1) == right) { | 
 | 2093 |       instruction->ReplaceWith(left->InputAt(0)); | 
 | 2094 |       RecordSimplification(); | 
 | 2095 |       instruction->GetBlock()->RemoveInstruction(instruction); | 
 | 2096 |       return; | 
 | 2097 |     } else if (left->InputAt(0) == right) { | 
 | 2098 |       instruction->ReplaceWith(left->InputAt(1)); | 
 | 2099 |       RecordSimplification(); | 
 | 2100 |       instruction->GetBlock()->RemoveInstruction(instruction); | 
 | 2101 |       return; | 
 | 2102 |     } | 
 | 2103 |   } | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 2104 | } | 
 | 2105 |  | 
 | 2106 | void InstructionSimplifierVisitor::VisitUShr(HUShr* instruction) { | 
 | 2107 |   VisitShift(instruction); | 
 | 2108 | } | 
 | 2109 |  | 
 | 2110 | void InstructionSimplifierVisitor::VisitXor(HXor* instruction) { | 
 | 2111 |   HConstant* input_cst = instruction->GetConstantRight(); | 
 | 2112 |   HInstruction* input_other = instruction->GetLeastConstantLeft(); | 
 | 2113 |  | 
| Roland Levillain | 1a65388 | 2016-03-18 18:05:57 +0000 | [diff] [blame] | 2114 |   if ((input_cst != nullptr) && input_cst->IsZeroBitPattern()) { | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 2115 |     // Replace code looking like | 
 | 2116 |     //    XOR dst, src, 0 | 
 | 2117 |     // with | 
 | 2118 |     //    src | 
 | 2119 |     instruction->ReplaceWith(input_other); | 
 | 2120 |     instruction->GetBlock()->RemoveInstruction(instruction); | 
| Alexandre Rames | c5809c3 | 2016-05-25 15:01:06 +0100 | [diff] [blame] | 2121 |     RecordSimplification(); | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 2122 |     return; | 
 | 2123 |   } | 
 | 2124 |  | 
| Sebastien Hertz | 9837caf | 2016-08-01 11:09:50 +0200 | [diff] [blame] | 2125 |   if ((input_cst != nullptr) && input_cst->IsOne() | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 2126 |       && input_other->GetType() == DataType::Type::kBool) { | 
| Sebastien Hertz | 9837caf | 2016-08-01 11:09:50 +0200 | [diff] [blame] | 2127 |     // Replace code looking like | 
 | 2128 |     //    XOR dst, src, 1 | 
 | 2129 |     // with | 
 | 2130 |     //    BOOLEAN_NOT dst, src | 
| Vladimir Marko | ca6fff8 | 2017-10-03 14:49:14 +0100 | [diff] [blame] | 2131 |     HBooleanNot* boolean_not = new (GetGraph()->GetAllocator()) HBooleanNot(input_other); | 
| Sebastien Hertz | 9837caf | 2016-08-01 11:09:50 +0200 | [diff] [blame] | 2132 |     instruction->GetBlock()->ReplaceAndRemoveInstructionWith(instruction, boolean_not); | 
 | 2133 |     RecordSimplification(); | 
 | 2134 |     return; | 
 | 2135 |   } | 
 | 2136 |  | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 2137 |   if ((input_cst != nullptr) && AreAllBitsSet(input_cst)) { | 
 | 2138 |     // Replace code looking like | 
 | 2139 |     //    XOR dst, src, 0xFFF...FF | 
 | 2140 |     // with | 
 | 2141 |     //    NOT dst, src | 
| Vladimir Marko | ca6fff8 | 2017-10-03 14:49:14 +0100 | [diff] [blame] | 2142 |     HNot* bitwise_not = new (GetGraph()->GetAllocator()) HNot(instruction->GetType(), input_other); | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 2143 |     instruction->GetBlock()->ReplaceAndRemoveInstructionWith(instruction, bitwise_not); | 
| Alexandre Rames | 188d431 | 2015-04-09 18:30:21 +0100 | [diff] [blame] | 2144 |     RecordSimplification(); | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 2145 |     return; | 
 | 2146 |   } | 
| Scott Wakeling | 40a04bf | 2015-12-11 09:50:36 +0000 | [diff] [blame] | 2147 |  | 
| Alexandre Rames | ca0e3a0 | 2016-02-03 10:54:07 +0000 | [diff] [blame] | 2148 |   HInstruction* left = instruction->GetLeft(); | 
 | 2149 |   HInstruction* right = instruction->GetRight(); | 
| Alexandre Rames | 9f98025 | 2016-02-05 14:00:28 +0000 | [diff] [blame] | 2150 |   if (((left->IsNot() && right->IsNot()) || | 
 | 2151 |        (left->IsBooleanNot() && right->IsBooleanNot())) && | 
| Alexandre Rames | ca0e3a0 | 2016-02-03 10:54:07 +0000 | [diff] [blame] | 2152 |       left->HasOnlyOneNonEnvironmentUse() && | 
 | 2153 |       right->HasOnlyOneNonEnvironmentUse()) { | 
 | 2154 |     // Replace code looking like | 
 | 2155 |     //    NOT nota, a | 
 | 2156 |     //    NOT notb, b | 
 | 2157 |     //    XOR dst, nota, notb | 
 | 2158 |     // with | 
 | 2159 |     //    XOR dst, a, b | 
| Alexandre Rames | 9f98025 | 2016-02-05 14:00:28 +0000 | [diff] [blame] | 2160 |     instruction->ReplaceInput(left->InputAt(0), 0); | 
 | 2161 |     instruction->ReplaceInput(right->InputAt(0), 1); | 
| Alexandre Rames | ca0e3a0 | 2016-02-03 10:54:07 +0000 | [diff] [blame] | 2162 |     left->GetBlock()->RemoveInstruction(left); | 
 | 2163 |     right->GetBlock()->RemoveInstruction(right); | 
 | 2164 |     RecordSimplification(); | 
 | 2165 |     return; | 
 | 2166 |   } | 
 | 2167 |  | 
| Anton Kirilov | e14dc86 | 2016-05-13 17:56:15 +0100 | [diff] [blame] | 2168 |   if (TryReplaceWithRotate(instruction)) { | 
 | 2169 |     return; | 
 | 2170 |   } | 
 | 2171 |  | 
 | 2172 |   // TryHandleAssociativeAndCommutativeOperation() does not remove its input, | 
 | 2173 |   // so no need to return. | 
 | 2174 |   TryHandleAssociativeAndCommutativeOperation(instruction); | 
| Alexandre Rames | b2fd7bc | 2015-03-11 16:48:16 +0000 | [diff] [blame] | 2175 | } | 
 | 2176 |  | 
| Nicolas Geoffray | ee3cf07 | 2015-10-06 11:45:02 +0100 | [diff] [blame] | 2177 | void InstructionSimplifierVisitor::SimplifyStringEquals(HInvoke* instruction) { | 
 | 2178 |   HInstruction* argument = instruction->InputAt(1); | 
 | 2179 |   HInstruction* receiver = instruction->InputAt(0); | 
 | 2180 |   if (receiver == argument) { | 
 | 2181 |     // Because String.equals is an instance call, the receiver is | 
 | 2182 |     // a null check if we don't know it's null. The argument however, will | 
 | 2183 |     // be the actual object. So we cannot end up in a situation where both | 
 | 2184 |     // are equal but could be null. | 
 | 2185 |     DCHECK(CanEnsureNotNullAt(argument, instruction)); | 
 | 2186 |     instruction->ReplaceWith(GetGraph()->GetIntConstant(1)); | 
 | 2187 |     instruction->GetBlock()->RemoveInstruction(instruction); | 
 | 2188 |   } else { | 
 | 2189 |     StringEqualsOptimizations optimizations(instruction); | 
 | 2190 |     if (CanEnsureNotNullAt(argument, instruction)) { | 
 | 2191 |       optimizations.SetArgumentNotNull(); | 
 | 2192 |     } | 
 | 2193 |     ScopedObjectAccess soa(Thread::Current()); | 
 | 2194 |     ReferenceTypeInfo argument_rti = argument->GetReferenceTypeInfo(); | 
 | 2195 |     if (argument_rti.IsValid() && argument_rti.IsStringClass()) { | 
 | 2196 |       optimizations.SetArgumentIsString(); | 
 | 2197 |     } | 
 | 2198 |   } | 
 | 2199 | } | 
 | 2200 |  | 
 | 2201 | static bool IsArrayLengthOf(HInstruction* potential_length, HInstruction* potential_array) { | 
 | 2202 |   if (potential_length->IsArrayLength()) { | 
 | 2203 |     return potential_length->InputAt(0) == potential_array; | 
 | 2204 |   } | 
 | 2205 |  | 
 | 2206 |   if (potential_array->IsNewArray()) { | 
| Nicolas Geoffray | e761bcc | 2017-01-19 08:59:37 +0000 | [diff] [blame] | 2207 |     return potential_array->AsNewArray()->GetLength() == potential_length; | 
| Nicolas Geoffray | ee3cf07 | 2015-10-06 11:45:02 +0100 | [diff] [blame] | 2208 |   } | 
 | 2209 |  | 
 | 2210 |   return false; | 
 | 2211 | } | 
 | 2212 |  | 
 | 2213 | void InstructionSimplifierVisitor::SimplifySystemArrayCopy(HInvoke* instruction) { | 
 | 2214 |   HInstruction* source = instruction->InputAt(0); | 
 | 2215 |   HInstruction* destination = instruction->InputAt(2); | 
 | 2216 |   HInstruction* count = instruction->InputAt(4); | 
 | 2217 |   SystemArrayCopyOptimizations optimizations(instruction); | 
 | 2218 |   if (CanEnsureNotNullAt(source, instruction)) { | 
 | 2219 |     optimizations.SetSourceIsNotNull(); | 
 | 2220 |   } | 
 | 2221 |   if (CanEnsureNotNullAt(destination, instruction)) { | 
 | 2222 |     optimizations.SetDestinationIsNotNull(); | 
 | 2223 |   } | 
 | 2224 |   if (destination == source) { | 
 | 2225 |     optimizations.SetDestinationIsSource(); | 
 | 2226 |   } | 
 | 2227 |  | 
 | 2228 |   if (IsArrayLengthOf(count, source)) { | 
 | 2229 |     optimizations.SetCountIsSourceLength(); | 
 | 2230 |   } | 
 | 2231 |  | 
 | 2232 |   if (IsArrayLengthOf(count, destination)) { | 
 | 2233 |     optimizations.SetCountIsDestinationLength(); | 
 | 2234 |   } | 
 | 2235 |  | 
 | 2236 |   { | 
 | 2237 |     ScopedObjectAccess soa(Thread::Current()); | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 2238 |     DataType::Type source_component_type = DataType::Type::kVoid; | 
 | 2239 |     DataType::Type destination_component_type = DataType::Type::kVoid; | 
| Nicolas Geoffray | ee3cf07 | 2015-10-06 11:45:02 +0100 | [diff] [blame] | 2240 |     ReferenceTypeInfo destination_rti = destination->GetReferenceTypeInfo(); | 
 | 2241 |     if (destination_rti.IsValid()) { | 
 | 2242 |       if (destination_rti.IsObjectArray()) { | 
 | 2243 |         if (destination_rti.IsExact()) { | 
 | 2244 |           optimizations.SetDoesNotNeedTypeCheck(); | 
 | 2245 |         } | 
 | 2246 |         optimizations.SetDestinationIsTypedObjectArray(); | 
| Nicolas Geoffray | a83a54d | 2015-10-02 17:30:26 +0100 | [diff] [blame] | 2247 |       } | 
| Nicolas Geoffray | ee3cf07 | 2015-10-06 11:45:02 +0100 | [diff] [blame] | 2248 |       if (destination_rti.IsPrimitiveArrayClass()) { | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 2249 |         destination_component_type = DataTypeFromPrimitive( | 
 | 2250 |             destination_rti.GetTypeHandle()->GetComponentType()->GetPrimitiveType()); | 
| Nicolas Geoffray | ee3cf07 | 2015-10-06 11:45:02 +0100 | [diff] [blame] | 2251 |         optimizations.SetDestinationIsPrimitiveArray(); | 
 | 2252 |       } else if (destination_rti.IsNonPrimitiveArrayClass()) { | 
 | 2253 |         optimizations.SetDestinationIsNonPrimitiveArray(); | 
| Nicolas Geoffray | a83a54d | 2015-10-02 17:30:26 +0100 | [diff] [blame] | 2254 |       } | 
 | 2255 |     } | 
| Nicolas Geoffray | ee3cf07 | 2015-10-06 11:45:02 +0100 | [diff] [blame] | 2256 |     ReferenceTypeInfo source_rti = source->GetReferenceTypeInfo(); | 
 | 2257 |     if (source_rti.IsValid()) { | 
 | 2258 |       if (destination_rti.IsValid() && destination_rti.CanArrayHoldValuesOf(source_rti)) { | 
 | 2259 |         optimizations.SetDoesNotNeedTypeCheck(); | 
 | 2260 |       } | 
 | 2261 |       if (source_rti.IsPrimitiveArrayClass()) { | 
 | 2262 |         optimizations.SetSourceIsPrimitiveArray(); | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 2263 |         source_component_type = DataTypeFromPrimitive( | 
 | 2264 |             source_rti.GetTypeHandle()->GetComponentType()->GetPrimitiveType()); | 
| Nicolas Geoffray | ee3cf07 | 2015-10-06 11:45:02 +0100 | [diff] [blame] | 2265 |       } else if (source_rti.IsNonPrimitiveArrayClass()) { | 
 | 2266 |         optimizations.SetSourceIsNonPrimitiveArray(); | 
 | 2267 |       } | 
 | 2268 |     } | 
| Nicolas Geoffray | c4aa82c | 2017-03-06 14:38:52 +0000 | [diff] [blame] | 2269 |     // For primitive arrays, use their optimized ArtMethod implementations. | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 2270 |     if ((source_component_type != DataType::Type::kVoid) && | 
| Nicolas Geoffray | c4aa82c | 2017-03-06 14:38:52 +0000 | [diff] [blame] | 2271 |         (source_component_type == destination_component_type)) { | 
 | 2272 |       ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); | 
 | 2273 |       PointerSize image_size = class_linker->GetImagePointerSize(); | 
 | 2274 |       HInvokeStaticOrDirect* invoke = instruction->AsInvokeStaticOrDirect(); | 
| Vladimir Marko | d93e374 | 2018-07-18 10:58:13 +0100 | [diff] [blame] | 2275 |       ObjPtr<mirror::Class> system = invoke->GetResolvedMethod()->GetDeclaringClass(); | 
| Nicolas Geoffray | c4aa82c | 2017-03-06 14:38:52 +0000 | [diff] [blame] | 2276 |       ArtMethod* method = nullptr; | 
 | 2277 |       switch (source_component_type) { | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 2278 |         case DataType::Type::kBool: | 
| Vladimir Marko | ba11882 | 2017-06-12 15:41:56 +0100 | [diff] [blame] | 2279 |           method = system->FindClassMethod("arraycopy", "([ZI[ZII)V", image_size); | 
| Nicolas Geoffray | c4aa82c | 2017-03-06 14:38:52 +0000 | [diff] [blame] | 2280 |           break; | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 2281 |         case DataType::Type::kInt8: | 
| Vladimir Marko | ba11882 | 2017-06-12 15:41:56 +0100 | [diff] [blame] | 2282 |           method = system->FindClassMethod("arraycopy", "([BI[BII)V", image_size); | 
| Nicolas Geoffray | c4aa82c | 2017-03-06 14:38:52 +0000 | [diff] [blame] | 2283 |           break; | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 2284 |         case DataType::Type::kUint16: | 
| Vladimir Marko | ba11882 | 2017-06-12 15:41:56 +0100 | [diff] [blame] | 2285 |           method = system->FindClassMethod("arraycopy", "([CI[CII)V", image_size); | 
| Nicolas Geoffray | c4aa82c | 2017-03-06 14:38:52 +0000 | [diff] [blame] | 2286 |           break; | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 2287 |         case DataType::Type::kInt16: | 
| Vladimir Marko | ba11882 | 2017-06-12 15:41:56 +0100 | [diff] [blame] | 2288 |           method = system->FindClassMethod("arraycopy", "([SI[SII)V", image_size); | 
| Nicolas Geoffray | c4aa82c | 2017-03-06 14:38:52 +0000 | [diff] [blame] | 2289 |           break; | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 2290 |         case DataType::Type::kInt32: | 
| Vladimir Marko | ba11882 | 2017-06-12 15:41:56 +0100 | [diff] [blame] | 2291 |           method = system->FindClassMethod("arraycopy", "([II[III)V", image_size); | 
| Nicolas Geoffray | c4aa82c | 2017-03-06 14:38:52 +0000 | [diff] [blame] | 2292 |           break; | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 2293 |         case DataType::Type::kFloat32: | 
| Vladimir Marko | ba11882 | 2017-06-12 15:41:56 +0100 | [diff] [blame] | 2294 |           method = system->FindClassMethod("arraycopy", "([FI[FII)V", image_size); | 
| Nicolas Geoffray | c4aa82c | 2017-03-06 14:38:52 +0000 | [diff] [blame] | 2295 |           break; | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 2296 |         case DataType::Type::kInt64: | 
| Vladimir Marko | ba11882 | 2017-06-12 15:41:56 +0100 | [diff] [blame] | 2297 |           method = system->FindClassMethod("arraycopy", "([JI[JII)V", image_size); | 
| Nicolas Geoffray | c4aa82c | 2017-03-06 14:38:52 +0000 | [diff] [blame] | 2298 |           break; | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 2299 |         case DataType::Type::kFloat64: | 
| Vladimir Marko | ba11882 | 2017-06-12 15:41:56 +0100 | [diff] [blame] | 2300 |           method = system->FindClassMethod("arraycopy", "([DI[DII)V", image_size); | 
| Nicolas Geoffray | c4aa82c | 2017-03-06 14:38:52 +0000 | [diff] [blame] | 2301 |           break; | 
 | 2302 |         default: | 
 | 2303 |           LOG(FATAL) << "Unreachable"; | 
 | 2304 |       } | 
 | 2305 |       DCHECK(method != nullptr); | 
| Vladimir Marko | ba11882 | 2017-06-12 15:41:56 +0100 | [diff] [blame] | 2306 |       DCHECK(method->IsStatic()); | 
 | 2307 |       DCHECK(method->GetDeclaringClass() == system); | 
| Nicolas Geoffray | c4aa82c | 2017-03-06 14:38:52 +0000 | [diff] [blame] | 2308 |       invoke->SetResolvedMethod(method); | 
 | 2309 |       // Sharpen the new invoke. Note that we do not update the dex method index of | 
 | 2310 |       // the invoke, as we would need to look it up in the current dex file, and it | 
 | 2311 |       // is unlikely that it exists. The most usual situation for such typed | 
 | 2312 |       // arraycopy methods is a direct pointer to the boot image. | 
| Nicolas Geoffray | bdb2ecc | 2018-09-18 14:33:55 +0100 | [diff] [blame] | 2313 |       invoke->SetDispatchInfo(HSharpening::SharpenInvokeStaticOrDirect(method, codegen_)); | 
| Nicolas Geoffray | c4aa82c | 2017-03-06 14:38:52 +0000 | [diff] [blame] | 2314 |     } | 
| Nicolas Geoffray | ee3cf07 | 2015-10-06 11:45:02 +0100 | [diff] [blame] | 2315 |   } | 
 | 2316 | } | 
 | 2317 |  | 
| Aart Bik | 2a6aad9 | 2016-02-25 11:32:32 -0800 | [diff] [blame] | 2318 | void InstructionSimplifierVisitor::SimplifyFP2Int(HInvoke* invoke) { | 
 | 2319 |   DCHECK(invoke->IsInvokeStaticOrDirect()); | 
 | 2320 |   uint32_t dex_pc = invoke->GetDexPc(); | 
 | 2321 |   HInstruction* x = invoke->InputAt(0); | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 2322 |   DataType::Type type = x->GetType(); | 
| Aart Bik | 2a6aad9 | 2016-02-25 11:32:32 -0800 | [diff] [blame] | 2323 |   // Set proper bit pattern for NaN and replace intrinsic with raw version. | 
 | 2324 |   HInstruction* nan; | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 2325 |   if (type == DataType::Type::kFloat64) { | 
| Aart Bik | 2a6aad9 | 2016-02-25 11:32:32 -0800 | [diff] [blame] | 2326 |     nan = GetGraph()->GetLongConstant(0x7ff8000000000000L); | 
 | 2327 |     invoke->SetIntrinsic(Intrinsics::kDoubleDoubleToRawLongBits, | 
 | 2328 |                          kNeedsEnvironmentOrCache, | 
 | 2329 |                          kNoSideEffects, | 
 | 2330 |                          kNoThrow); | 
 | 2331 |   } else { | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 2332 |     DCHECK_EQ(type, DataType::Type::kFloat32); | 
| Aart Bik | 2a6aad9 | 2016-02-25 11:32:32 -0800 | [diff] [blame] | 2333 |     nan = GetGraph()->GetIntConstant(0x7fc00000); | 
 | 2334 |     invoke->SetIntrinsic(Intrinsics::kFloatFloatToRawIntBits, | 
 | 2335 |                          kNeedsEnvironmentOrCache, | 
 | 2336 |                          kNoSideEffects, | 
 | 2337 |                          kNoThrow); | 
 | 2338 |   } | 
 | 2339 |   // Test IsNaN(x), which is the same as x != x. | 
| Vladimir Marko | ca6fff8 | 2017-10-03 14:49:14 +0100 | [diff] [blame] | 2340 |   HCondition* condition = new (GetGraph()->GetAllocator()) HNotEqual(x, x, dex_pc); | 
| Aart Bik | 2a6aad9 | 2016-02-25 11:32:32 -0800 | [diff] [blame] | 2341 |   condition->SetBias(ComparisonBias::kLtBias); | 
 | 2342 |   invoke->GetBlock()->InsertInstructionBefore(condition, invoke->GetNext()); | 
 | 2343 |   // Select between the two. | 
| Vladimir Marko | ca6fff8 | 2017-10-03 14:49:14 +0100 | [diff] [blame] | 2344 |   HInstruction* select = new (GetGraph()->GetAllocator()) HSelect(condition, nan, invoke, dex_pc); | 
| Aart Bik | 2a6aad9 | 2016-02-25 11:32:32 -0800 | [diff] [blame] | 2345 |   invoke->GetBlock()->InsertInstructionBefore(select, condition->GetNext()); | 
 | 2346 |   invoke->ReplaceWithExceptInReplacementAtIndex(select, 0);  // false at index 0 | 
 | 2347 | } | 
 | 2348 |  | 
| Vladimir Marko | 87f3fcb | 2016-04-28 15:52:11 +0100 | [diff] [blame] | 2349 | void InstructionSimplifierVisitor::SimplifyStringCharAt(HInvoke* invoke) { | 
 | 2350 |   HInstruction* str = invoke->InputAt(0); | 
 | 2351 |   HInstruction* index = invoke->InputAt(1); | 
 | 2352 |   uint32_t dex_pc = invoke->GetDexPc(); | 
| Vladimir Marko | e764d2e | 2017-10-05 14:35:55 +0100 | [diff] [blame] | 2353 |   ArenaAllocator* allocator = GetGraph()->GetAllocator(); | 
| Vladimir Marko | 87f3fcb | 2016-04-28 15:52:11 +0100 | [diff] [blame] | 2354 |   // We treat String as an array to allow DCE and BCE to seamlessly work on strings, | 
 | 2355 |   // so create the HArrayLength, HBoundsCheck and HArrayGet. | 
| Andreas Gampe | 3db7068 | 2018-12-26 15:12:03 -0800 | [diff] [blame] | 2356 |   HArrayLength* length = new (allocator) HArrayLength(str, dex_pc, /* is_string_length= */ true); | 
| Vladimir Marko | 87f3fcb | 2016-04-28 15:52:11 +0100 | [diff] [blame] | 2357 |   invoke->GetBlock()->InsertInstructionBefore(length, invoke); | 
| Vladimir Marko | e764d2e | 2017-10-05 14:35:55 +0100 | [diff] [blame] | 2358 |   HBoundsCheck* bounds_check = new (allocator) HBoundsCheck( | 
| Andreas Gampe | 3db7068 | 2018-12-26 15:12:03 -0800 | [diff] [blame] | 2359 |       index, length, dex_pc, /* is_string_char_at= */ true); | 
| Vladimir Marko | 87f3fcb | 2016-04-28 15:52:11 +0100 | [diff] [blame] | 2360 |   invoke->GetBlock()->InsertInstructionBefore(bounds_check, invoke); | 
| Vladimir Marko | e764d2e | 2017-10-05 14:35:55 +0100 | [diff] [blame] | 2361 |   HArrayGet* array_get = new (allocator) HArrayGet(str, | 
 | 2362 |                                                    bounds_check, | 
 | 2363 |                                                    DataType::Type::kUint16, | 
 | 2364 |                                                    SideEffects::None(),  // Strings are immutable. | 
 | 2365 |                                                    dex_pc, | 
| Andreas Gampe | 3db7068 | 2018-12-26 15:12:03 -0800 | [diff] [blame] | 2366 |                                                    /* is_string_char_at= */ true); | 
| Vladimir Marko | 87f3fcb | 2016-04-28 15:52:11 +0100 | [diff] [blame] | 2367 |   invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, array_get); | 
 | 2368 |   bounds_check->CopyEnvironmentFrom(invoke->GetEnvironment()); | 
 | 2369 |   GetGraph()->SetHasBoundsChecks(true); | 
 | 2370 | } | 
 | 2371 |  | 
| Vladimir Marko | 5f84607 | 2020-04-09 13:20:11 +0100 | [diff] [blame] | 2372 | void InstructionSimplifierVisitor::SimplifyStringLength(HInvoke* invoke) { | 
| Vladimir Marko | dce016e | 2016-04-28 13:10:02 +0100 | [diff] [blame] | 2373 |   HInstruction* str = invoke->InputAt(0); | 
 | 2374 |   uint32_t dex_pc = invoke->GetDexPc(); | 
 | 2375 |   // We treat String as an array to allow DCE and BCE to seamlessly work on strings, | 
 | 2376 |   // so create the HArrayLength. | 
| Vladimir Marko | 87f3fcb | 2016-04-28 15:52:11 +0100 | [diff] [blame] | 2377 |   HArrayLength* length = | 
| Andreas Gampe | 3db7068 | 2018-12-26 15:12:03 -0800 | [diff] [blame] | 2378 |       new (GetGraph()->GetAllocator()) HArrayLength(str, dex_pc, /* is_string_length= */ true); | 
| Vladimir Marko | 5f84607 | 2020-04-09 13:20:11 +0100 | [diff] [blame] | 2379 |   invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, length); | 
| Vladimir Marko | dce016e | 2016-04-28 13:10:02 +0100 | [diff] [blame] | 2380 | } | 
 | 2381 |  | 
| Vladimir Marko | 6fa4404 | 2018-03-19 18:42:49 +0000 | [diff] [blame] | 2382 | void InstructionSimplifierVisitor::SimplifyStringIndexOf(HInvoke* invoke) { | 
 | 2383 |   DCHECK(invoke->GetIntrinsic() == Intrinsics::kStringIndexOf || | 
 | 2384 |          invoke->GetIntrinsic() == Intrinsics::kStringIndexOfAfter); | 
 | 2385 |   if (invoke->InputAt(0)->IsLoadString()) { | 
 | 2386 |     HLoadString* load_string = invoke->InputAt(0)->AsLoadString(); | 
 | 2387 |     const DexFile& dex_file = load_string->GetDexFile(); | 
 | 2388 |     uint32_t utf16_length; | 
 | 2389 |     const char* data = | 
 | 2390 |         dex_file.StringDataAndUtf16LengthByIdx(load_string->GetStringIndex(), &utf16_length); | 
 | 2391 |     if (utf16_length == 0) { | 
 | 2392 |       invoke->ReplaceWith(GetGraph()->GetIntConstant(-1)); | 
 | 2393 |       invoke->GetBlock()->RemoveInstruction(invoke); | 
 | 2394 |       RecordSimplification(); | 
 | 2395 |       return; | 
 | 2396 |     } | 
 | 2397 |     if (utf16_length == 1 && invoke->GetIntrinsic() == Intrinsics::kStringIndexOf) { | 
 | 2398 |       // Simplify to HSelect(HEquals(., load_string.charAt(0)), 0, -1). | 
 | 2399 |       // If the sought character is supplementary, this gives the correct result, i.e. -1. | 
 | 2400 |       uint32_t c = GetUtf16FromUtf8(&data); | 
 | 2401 |       DCHECK_EQ(GetTrailingUtf16Char(c), 0u); | 
 | 2402 |       DCHECK_EQ(GetLeadingUtf16Char(c), c); | 
 | 2403 |       uint32_t dex_pc = invoke->GetDexPc(); | 
 | 2404 |       ArenaAllocator* allocator = GetGraph()->GetAllocator(); | 
 | 2405 |       HEqual* equal = | 
 | 2406 |           new (allocator) HEqual(invoke->InputAt(1), GetGraph()->GetIntConstant(c), dex_pc); | 
 | 2407 |       invoke->GetBlock()->InsertInstructionBefore(equal, invoke); | 
 | 2408 |       HSelect* result = new (allocator) HSelect(equal, | 
 | 2409 |                                                 GetGraph()->GetIntConstant(0), | 
 | 2410 |                                                 GetGraph()->GetIntConstant(-1), | 
 | 2411 |                                                 dex_pc); | 
 | 2412 |       invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, result); | 
 | 2413 |       RecordSimplification(); | 
 | 2414 |       return; | 
 | 2415 |     } | 
 | 2416 |   } | 
 | 2417 | } | 
 | 2418 |  | 
| Aart Bik | ff7d89c | 2016-11-07 08:49:28 -0800 | [diff] [blame] | 2419 | // This method should only be used on intrinsics whose sole way of throwing an | 
 | 2420 | // exception is raising a NPE when the nth argument is null. If that argument | 
 | 2421 | // is provably non-null, we can clear the flag. | 
 | 2422 | void InstructionSimplifierVisitor::SimplifyNPEOnArgN(HInvoke* invoke, size_t n) { | 
 | 2423 |   HInstruction* arg = invoke->InputAt(n); | 
| Aart Bik | 71bf7b4 | 2016-11-16 10:17:46 -0800 | [diff] [blame] | 2424 |   if (invoke->CanThrow() && !arg->CanBeNull()) { | 
| Aart Bik | ff7d89c | 2016-11-07 08:49:28 -0800 | [diff] [blame] | 2425 |     invoke->SetCanThrow(false); | 
 | 2426 |   } | 
 | 2427 | } | 
 | 2428 |  | 
| Aart Bik | 71bf7b4 | 2016-11-16 10:17:46 -0800 | [diff] [blame] | 2429 | // Methods that return "this" can replace the returned value with the receiver. | 
 | 2430 | void InstructionSimplifierVisitor::SimplifyReturnThis(HInvoke* invoke) { | 
 | 2431 |   if (invoke->HasUses()) { | 
 | 2432 |     HInstruction* receiver = invoke->InputAt(0); | 
 | 2433 |     invoke->ReplaceWith(receiver); | 
 | 2434 |     RecordSimplification(); | 
 | 2435 |   } | 
 | 2436 | } | 
 | 2437 |  | 
 | 2438 | // Helper method for StringBuffer escape analysis. | 
 | 2439 | static bool NoEscapeForStringBufferReference(HInstruction* reference, HInstruction* user) { | 
 | 2440 |   if (user->IsInvokeStaticOrDirect()) { | 
 | 2441 |     // Any constructor on StringBuffer is okay. | 
| Aart Bik | ab2270f | 2016-12-15 09:36:31 -0800 | [diff] [blame] | 2442 |     return user->AsInvokeStaticOrDirect()->GetResolvedMethod() != nullptr && | 
 | 2443 |            user->AsInvokeStaticOrDirect()->GetResolvedMethod()->IsConstructor() && | 
| Aart Bik | 71bf7b4 | 2016-11-16 10:17:46 -0800 | [diff] [blame] | 2444 |            user->InputAt(0) == reference; | 
 | 2445 |   } else if (user->IsInvokeVirtual()) { | 
 | 2446 |     switch (user->AsInvokeVirtual()->GetIntrinsic()) { | 
 | 2447 |       case Intrinsics::kStringBufferLength: | 
 | 2448 |       case Intrinsics::kStringBufferToString: | 
 | 2449 |         DCHECK_EQ(user->InputAt(0), reference); | 
 | 2450 |         return true; | 
 | 2451 |       case Intrinsics::kStringBufferAppend: | 
 | 2452 |         // Returns "this", so only okay if no further uses. | 
 | 2453 |         DCHECK_EQ(user->InputAt(0), reference); | 
 | 2454 |         DCHECK_NE(user->InputAt(1), reference); | 
 | 2455 |         return !user->HasUses(); | 
 | 2456 |       default: | 
 | 2457 |         break; | 
 | 2458 |     } | 
 | 2459 |   } | 
 | 2460 |   return false; | 
 | 2461 | } | 
 | 2462 |  | 
| Vladimir Marko | 552a134 | 2017-10-31 10:56:47 +0000 | [diff] [blame] | 2463 | static bool TryReplaceStringBuilderAppend(HInvoke* invoke) { | 
 | 2464 |   DCHECK_EQ(invoke->GetIntrinsic(), Intrinsics::kStringBuilderToString); | 
 | 2465 |   if (invoke->CanThrowIntoCatchBlock()) { | 
 | 2466 |     return false; | 
 | 2467 |   } | 
 | 2468 |  | 
 | 2469 |   HBasicBlock* block = invoke->GetBlock(); | 
 | 2470 |   HInstruction* sb = invoke->InputAt(0); | 
 | 2471 |  | 
 | 2472 |   // We support only a new StringBuilder, otherwise we cannot ensure that | 
 | 2473 |   // the StringBuilder data does not need to be populated for other users. | 
 | 2474 |   if (!sb->IsNewInstance()) { | 
 | 2475 |     return false; | 
 | 2476 |   } | 
 | 2477 |  | 
 | 2478 |   // For now, we support only single-block recognition. | 
 | 2479 |   // (Ternary operators feeding the append could be implemented.) | 
 | 2480 |   for (const HUseListNode<HInstruction*>& use : sb->GetUses()) { | 
 | 2481 |     if (use.GetUser()->GetBlock() != block) { | 
 | 2482 |       return false; | 
 | 2483 |     } | 
| Vladimir Marko | a18f5ae | 2019-12-13 12:53:39 +0000 | [diff] [blame] | 2484 |     // The append pattern uses the StringBuilder only as the first argument. | 
 | 2485 |     if (use.GetIndex() != 0u) { | 
 | 2486 |       return false; | 
 | 2487 |     } | 
| Vladimir Marko | 552a134 | 2017-10-31 10:56:47 +0000 | [diff] [blame] | 2488 |   } | 
 | 2489 |  | 
 | 2490 |   // Collect args and check for unexpected uses. | 
 | 2491 |   // We expect one call to a constructor with no arguments, one constructor fence (unless | 
 | 2492 |   // eliminated), some number of append calls and one call to StringBuilder.toString(). | 
 | 2493 |   bool seen_constructor = false; | 
 | 2494 |   bool seen_constructor_fence = false; | 
 | 2495 |   bool seen_to_string = false; | 
 | 2496 |   uint32_t format = 0u; | 
 | 2497 |   uint32_t num_args = 0u; | 
 | 2498 |   HInstruction* args[StringBuilderAppend::kMaxArgs];  // Added in reverse order. | 
| Vladimir Marko | a18f5ae | 2019-12-13 12:53:39 +0000 | [diff] [blame] | 2499 |   for (HBackwardInstructionIterator iter(block->GetInstructions()); !iter.Done(); iter.Advance()) { | 
 | 2500 |     HInstruction* user = iter.Current(); | 
 | 2501 |     // Instructions of interest apply to `sb`, skip those that do not involve `sb`. | 
 | 2502 |     if (user->InputCount() == 0u || user->InputAt(0u) != sb) { | 
 | 2503 |       continue; | 
| Vladimir Marko | 552a134 | 2017-10-31 10:56:47 +0000 | [diff] [blame] | 2504 |     } | 
| Vladimir Marko | a18f5ae | 2019-12-13 12:53:39 +0000 | [diff] [blame] | 2505 |     // We visit the uses in reverse order, so the StringBuilder.toString() must come first. | 
| Vladimir Marko | 552a134 | 2017-10-31 10:56:47 +0000 | [diff] [blame] | 2506 |     if (!seen_to_string) { | 
| Vladimir Marko | a18f5ae | 2019-12-13 12:53:39 +0000 | [diff] [blame] | 2507 |       if (user == invoke) { | 
| Vladimir Marko | 552a134 | 2017-10-31 10:56:47 +0000 | [diff] [blame] | 2508 |         seen_to_string = true; | 
 | 2509 |         continue; | 
 | 2510 |       } else { | 
 | 2511 |         return false; | 
 | 2512 |       } | 
 | 2513 |     } | 
 | 2514 |     // Then we should see the arguments. | 
 | 2515 |     if (user->IsInvokeVirtual()) { | 
 | 2516 |       HInvokeVirtual* as_invoke_virtual = user->AsInvokeVirtual(); | 
 | 2517 |       DCHECK(!seen_constructor); | 
 | 2518 |       DCHECK(!seen_constructor_fence); | 
 | 2519 |       StringBuilderAppend::Argument arg; | 
 | 2520 |       switch (as_invoke_virtual->GetIntrinsic()) { | 
 | 2521 |         case Intrinsics::kStringBuilderAppendObject: | 
 | 2522 |           // TODO: Unimplemented, needs to call String.valueOf(). | 
 | 2523 |           return false; | 
 | 2524 |         case Intrinsics::kStringBuilderAppendString: | 
 | 2525 |           arg = StringBuilderAppend::Argument::kString; | 
 | 2526 |           break; | 
 | 2527 |         case Intrinsics::kStringBuilderAppendCharArray: | 
 | 2528 |           // TODO: Unimplemented, StringBuilder.append(char[]) can throw NPE and we would | 
 | 2529 |           // not have the correct stack trace for it. | 
 | 2530 |           return false; | 
 | 2531 |         case Intrinsics::kStringBuilderAppendBoolean: | 
 | 2532 |           arg = StringBuilderAppend::Argument::kBoolean; | 
 | 2533 |           break; | 
 | 2534 |         case Intrinsics::kStringBuilderAppendChar: | 
 | 2535 |           arg = StringBuilderAppend::Argument::kChar; | 
 | 2536 |           break; | 
 | 2537 |         case Intrinsics::kStringBuilderAppendInt: | 
 | 2538 |           arg = StringBuilderAppend::Argument::kInt; | 
 | 2539 |           break; | 
 | 2540 |         case Intrinsics::kStringBuilderAppendLong: | 
 | 2541 |           arg = StringBuilderAppend::Argument::kLong; | 
 | 2542 |           break; | 
 | 2543 |         case Intrinsics::kStringBuilderAppendCharSequence: { | 
 | 2544 |           ReferenceTypeInfo rti = user->AsInvokeVirtual()->InputAt(1)->GetReferenceTypeInfo(); | 
 | 2545 |           if (!rti.IsValid()) { | 
 | 2546 |             return false; | 
 | 2547 |           } | 
 | 2548 |           ScopedObjectAccess soa(Thread::Current()); | 
 | 2549 |           Handle<mirror::Class> input_type = rti.GetTypeHandle(); | 
 | 2550 |           DCHECK(input_type != nullptr); | 
 | 2551 |           if (input_type.Get() == GetClassRoot<mirror::String>()) { | 
 | 2552 |             arg = StringBuilderAppend::Argument::kString; | 
 | 2553 |           } else { | 
 | 2554 |             // TODO: Check and implement for StringBuilder. We could find the StringBuilder's | 
 | 2555 |             // internal char[] inconsistent with the length, or the string compression | 
 | 2556 |             // of the result could be compromised with a concurrent modification, and | 
 | 2557 |             // we would need to throw appropriate exceptions. | 
 | 2558 |             return false; | 
 | 2559 |           } | 
 | 2560 |           break; | 
 | 2561 |         } | 
 | 2562 |         case Intrinsics::kStringBuilderAppendFloat: | 
 | 2563 |         case Intrinsics::kStringBuilderAppendDouble: | 
 | 2564 |           // TODO: Unimplemented, needs to call FloatingDecimal.getBinaryToASCIIConverter(). | 
 | 2565 |           return false; | 
 | 2566 |         default: { | 
 | 2567 |           return false; | 
 | 2568 |         } | 
 | 2569 |       } | 
 | 2570 |       // Uses of the append return value should have been replaced with the first input. | 
 | 2571 |       DCHECK(!as_invoke_virtual->HasUses()); | 
 | 2572 |       DCHECK(!as_invoke_virtual->HasEnvironmentUses()); | 
 | 2573 |       if (num_args == StringBuilderAppend::kMaxArgs) { | 
 | 2574 |         return false; | 
 | 2575 |       } | 
 | 2576 |       format = (format << StringBuilderAppend::kBitsPerArg) | static_cast<uint32_t>(arg); | 
 | 2577 |       args[num_args] = as_invoke_virtual->InputAt(1u); | 
 | 2578 |       ++num_args; | 
 | 2579 |     } else if (user->IsInvokeStaticOrDirect() && | 
 | 2580 |                user->AsInvokeStaticOrDirect()->GetResolvedMethod() != nullptr && | 
 | 2581 |                user->AsInvokeStaticOrDirect()->GetResolvedMethod()->IsConstructor() && | 
 | 2582 |                user->AsInvokeStaticOrDirect()->GetNumberOfArguments() == 1u) { | 
 | 2583 |       // After arguments, we should see the constructor. | 
 | 2584 |       // We accept only the constructor with no extra arguments. | 
 | 2585 |       DCHECK(!seen_constructor); | 
 | 2586 |       DCHECK(!seen_constructor_fence); | 
 | 2587 |       seen_constructor = true; | 
 | 2588 |     } else if (user->IsConstructorFence()) { | 
 | 2589 |       // The last use we see is the constructor fence. | 
 | 2590 |       DCHECK(seen_constructor); | 
 | 2591 |       DCHECK(!seen_constructor_fence); | 
 | 2592 |       seen_constructor_fence = true; | 
 | 2593 |     } else { | 
 | 2594 |       return false; | 
 | 2595 |     } | 
 | 2596 |   } | 
 | 2597 |  | 
 | 2598 |   if (num_args == 0u) { | 
 | 2599 |     return false; | 
 | 2600 |   } | 
 | 2601 |  | 
 | 2602 |   // Check environment uses. | 
 | 2603 |   for (const HUseListNode<HEnvironment*>& use : sb->GetEnvUses()) { | 
 | 2604 |     HInstruction* holder = use.GetUser()->GetHolder(); | 
 | 2605 |     if (holder->GetBlock() != block) { | 
 | 2606 |       return false; | 
 | 2607 |     } | 
 | 2608 |     // Accept only calls on the StringBuilder (which shall all be removed). | 
 | 2609 |     // TODO: Carve-out for const-string? Or rely on environment pruning (to be implemented)? | 
 | 2610 |     if (holder->InputCount() == 0 || holder->InputAt(0) != sb) { | 
 | 2611 |       return false; | 
 | 2612 |     } | 
 | 2613 |   } | 
 | 2614 |  | 
 | 2615 |   // Create replacement instruction. | 
 | 2616 |   HIntConstant* fmt = block->GetGraph()->GetIntConstant(static_cast<int32_t>(format)); | 
 | 2617 |   ArenaAllocator* allocator = block->GetGraph()->GetAllocator(); | 
 | 2618 |   HStringBuilderAppend* append = | 
 | 2619 |       new (allocator) HStringBuilderAppend(fmt, num_args, allocator, invoke->GetDexPc()); | 
 | 2620 |   append->SetReferenceTypeInfo(invoke->GetReferenceTypeInfo()); | 
 | 2621 |   for (size_t i = 0; i != num_args; ++i) { | 
 | 2622 |     append->SetArgumentAt(i, args[num_args - 1u - i]); | 
 | 2623 |   } | 
 | 2624 |   block->InsertInstructionBefore(append, invoke); | 
| Vladimir Marko | b1fe5e1 | 2020-03-10 14:30:49 +0000 | [diff] [blame] | 2625 |   DCHECK(!invoke->CanBeNull()); | 
 | 2626 |   DCHECK(!append->CanBeNull()); | 
| Vladimir Marko | 552a134 | 2017-10-31 10:56:47 +0000 | [diff] [blame] | 2627 |   invoke->ReplaceWith(append); | 
 | 2628 |   // Copy environment, except for the StringBuilder uses. | 
| Vladimir Marko | 56f1332 | 2019-11-15 14:07:19 +0000 | [diff] [blame] | 2629 |   for (HEnvironment* env = invoke->GetEnvironment(); env != nullptr; env = env->GetParent()) { | 
 | 2630 |     for (size_t i = 0, size = env->Size(); i != size; ++i) { | 
 | 2631 |       if (env->GetInstructionAt(i) == sb) { | 
 | 2632 |         env->RemoveAsUserOfInput(i); | 
 | 2633 |         env->SetRawEnvAt(i, /*instruction=*/ nullptr); | 
 | 2634 |       } | 
| Vladimir Marko | 552a134 | 2017-10-31 10:56:47 +0000 | [diff] [blame] | 2635 |     } | 
 | 2636 |   } | 
 | 2637 |   append->CopyEnvironmentFrom(invoke->GetEnvironment()); | 
 | 2638 |   // Remove the old instruction. | 
 | 2639 |   block->RemoveInstruction(invoke); | 
 | 2640 |   // Remove the StringBuilder's uses and StringBuilder. | 
 | 2641 |   while (sb->HasNonEnvironmentUses()) { | 
 | 2642 |     block->RemoveInstruction(sb->GetUses().front().GetUser()); | 
 | 2643 |   } | 
 | 2644 |   DCHECK(!sb->HasEnvironmentUses()); | 
 | 2645 |   block->RemoveInstruction(sb); | 
 | 2646 |   return true; | 
 | 2647 | } | 
 | 2648 |  | 
| Aart Bik | 71bf7b4 | 2016-11-16 10:17:46 -0800 | [diff] [blame] | 2649 | // Certain allocation intrinsics are not removed by dead code elimination | 
 | 2650 | // because of potentially throwing an OOM exception or other side effects. | 
 | 2651 | // This method removes such intrinsics when special circumstances allow. | 
 | 2652 | void InstructionSimplifierVisitor::SimplifyAllocationIntrinsic(HInvoke* invoke) { | 
 | 2653 |   if (!invoke->HasUses()) { | 
 | 2654 |     // Instruction has no uses. If unsynchronized, we can remove right away, safely ignoring | 
 | 2655 |     // the potential OOM of course. Otherwise, we must ensure the receiver object of this | 
 | 2656 |     // call does not escape since only thread-local synchronization may be removed. | 
 | 2657 |     bool is_synchronized = invoke->GetIntrinsic() == Intrinsics::kStringBufferToString; | 
 | 2658 |     HInstruction* receiver = invoke->InputAt(0); | 
 | 2659 |     if (!is_synchronized || DoesNotEscape(receiver, NoEscapeForStringBufferReference)) { | 
 | 2660 |       invoke->GetBlock()->RemoveInstruction(invoke); | 
 | 2661 |       RecordSimplification(); | 
 | 2662 |     } | 
| Vladimir Marko | 552a134 | 2017-10-31 10:56:47 +0000 | [diff] [blame] | 2663 |   } else if (invoke->GetIntrinsic() == Intrinsics::kStringBuilderToString && | 
 | 2664 |              TryReplaceStringBuilderAppend(invoke)) { | 
 | 2665 |     RecordSimplification(); | 
| Aart Bik | 71bf7b4 | 2016-11-16 10:17:46 -0800 | [diff] [blame] | 2666 |   } | 
 | 2667 | } | 
 | 2668 |  | 
| Nicolas Geoffray | ee3cf07 | 2015-10-06 11:45:02 +0100 | [diff] [blame] | 2669 | void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) { | 
| Aart Bik | 2a6aad9 | 2016-02-25 11:32:32 -0800 | [diff] [blame] | 2670 |   switch (instruction->GetIntrinsic()) { | 
 | 2671 |     case Intrinsics::kStringEquals: | 
 | 2672 |       SimplifyStringEquals(instruction); | 
 | 2673 |       break; | 
 | 2674 |     case Intrinsics::kSystemArrayCopy: | 
 | 2675 |       SimplifySystemArrayCopy(instruction); | 
 | 2676 |       break; | 
| Aart Bik | 2a6aad9 | 2016-02-25 11:32:32 -0800 | [diff] [blame] | 2677 |     case Intrinsics::kFloatFloatToIntBits: | 
 | 2678 |     case Intrinsics::kDoubleDoubleToLongBits: | 
 | 2679 |       SimplifyFP2Int(instruction); | 
 | 2680 |       break; | 
| Vladimir Marko | 87f3fcb | 2016-04-28 15:52:11 +0100 | [diff] [blame] | 2681 |     case Intrinsics::kStringCharAt: | 
| Vladimir Marko | 5f84607 | 2020-04-09 13:20:11 +0100 | [diff] [blame] | 2682 |       // Instruction builder creates intermediate representation directly | 
 | 2683 |       // but the inliner can sharpen CharSequence.charAt() to String.charAt(). | 
| Vladimir Marko | 87f3fcb | 2016-04-28 15:52:11 +0100 | [diff] [blame] | 2684 |       SimplifyStringCharAt(instruction); | 
 | 2685 |       break; | 
| Vladimir Marko | dce016e | 2016-04-28 13:10:02 +0100 | [diff] [blame] | 2686 |     case Intrinsics::kStringLength: | 
| Vladimir Marko | 5f84607 | 2020-04-09 13:20:11 +0100 | [diff] [blame] | 2687 |       // Instruction builder creates intermediate representation directly | 
 | 2688 |       // but the inliner can sharpen CharSequence.length() to String.length(). | 
 | 2689 |       SimplifyStringLength(instruction); | 
| Vladimir Marko | dce016e | 2016-04-28 13:10:02 +0100 | [diff] [blame] | 2690 |       break; | 
| Vladimir Marko | 6fa4404 | 2018-03-19 18:42:49 +0000 | [diff] [blame] | 2691 |     case Intrinsics::kStringIndexOf: | 
 | 2692 |     case Intrinsics::kStringIndexOfAfter: | 
 | 2693 |       SimplifyStringIndexOf(instruction); | 
 | 2694 |       break; | 
| Aart Bik | ff7d89c | 2016-11-07 08:49:28 -0800 | [diff] [blame] | 2695 |     case Intrinsics::kStringStringIndexOf: | 
 | 2696 |     case Intrinsics::kStringStringIndexOfAfter: | 
 | 2697 |       SimplifyNPEOnArgN(instruction, 1);  // 0th has own NullCheck | 
 | 2698 |       break; | 
| Aart Bik | 71bf7b4 | 2016-11-16 10:17:46 -0800 | [diff] [blame] | 2699 |     case Intrinsics::kStringBufferAppend: | 
| Vladimir Marko | d456117 | 2017-10-30 17:48:25 +0000 | [diff] [blame] | 2700 |     case Intrinsics::kStringBuilderAppendObject: | 
 | 2701 |     case Intrinsics::kStringBuilderAppendString: | 
 | 2702 |     case Intrinsics::kStringBuilderAppendCharSequence: | 
 | 2703 |     case Intrinsics::kStringBuilderAppendCharArray: | 
 | 2704 |     case Intrinsics::kStringBuilderAppendBoolean: | 
 | 2705 |     case Intrinsics::kStringBuilderAppendChar: | 
 | 2706 |     case Intrinsics::kStringBuilderAppendInt: | 
 | 2707 |     case Intrinsics::kStringBuilderAppendLong: | 
 | 2708 |     case Intrinsics::kStringBuilderAppendFloat: | 
 | 2709 |     case Intrinsics::kStringBuilderAppendDouble: | 
| Aart Bik | 71bf7b4 | 2016-11-16 10:17:46 -0800 | [diff] [blame] | 2710 |       SimplifyReturnThis(instruction); | 
 | 2711 |       break; | 
 | 2712 |     case Intrinsics::kStringBufferToString: | 
 | 2713 |     case Intrinsics::kStringBuilderToString: | 
 | 2714 |       SimplifyAllocationIntrinsic(instruction); | 
 | 2715 |       break; | 
| Vladimir Marko | 5f84607 | 2020-04-09 13:20:11 +0100 | [diff] [blame] | 2716 |     case Intrinsics::kIntegerRotateRight: | 
 | 2717 |     case Intrinsics::kLongRotateRight: | 
 | 2718 |     case Intrinsics::kIntegerRotateLeft: | 
 | 2719 |     case Intrinsics::kLongRotateLeft: | 
 | 2720 |     case Intrinsics::kIntegerCompare: | 
 | 2721 |     case Intrinsics::kLongCompare: | 
 | 2722 |     case Intrinsics::kIntegerSignum: | 
 | 2723 |     case Intrinsics::kLongSignum: | 
 | 2724 |     case Intrinsics::kFloatIsNaN: | 
 | 2725 |     case Intrinsics::kDoubleIsNaN: | 
 | 2726 |     case Intrinsics::kStringIsEmpty: | 
| Aart Bik | 1193259 | 2016-03-08 12:42:25 -0800 | [diff] [blame] | 2727 |     case Intrinsics::kUnsafeLoadFence: | 
| Aart Bik | 1193259 | 2016-03-08 12:42:25 -0800 | [diff] [blame] | 2728 |     case Intrinsics::kUnsafeStoreFence: | 
| Aart Bik | 1193259 | 2016-03-08 12:42:25 -0800 | [diff] [blame] | 2729 |     case Intrinsics::kUnsafeFullFence: | 
| Orion Hodson | 4a4610a | 2017-09-28 16:57:55 +0100 | [diff] [blame] | 2730 |     case Intrinsics::kVarHandleFullFence: | 
| Orion Hodson | 4a4610a | 2017-09-28 16:57:55 +0100 | [diff] [blame] | 2731 |     case Intrinsics::kVarHandleAcquireFence: | 
| Orion Hodson | 4a4610a | 2017-09-28 16:57:55 +0100 | [diff] [blame] | 2732 |     case Intrinsics::kVarHandleReleaseFence: | 
| Orion Hodson | 4a4610a | 2017-09-28 16:57:55 +0100 | [diff] [blame] | 2733 |     case Intrinsics::kVarHandleLoadLoadFence: | 
| Orion Hodson | 4a4610a | 2017-09-28 16:57:55 +0100 | [diff] [blame] | 2734 |     case Intrinsics::kVarHandleStoreStoreFence: | 
| Aart Bik | 1f8d51b | 2018-02-15 10:42:37 -0800 | [diff] [blame] | 2735 |     case Intrinsics::kMathMinIntInt: | 
| Aart Bik | 1f8d51b | 2018-02-15 10:42:37 -0800 | [diff] [blame] | 2736 |     case Intrinsics::kMathMinLongLong: | 
| Aart Bik | 1f8d51b | 2018-02-15 10:42:37 -0800 | [diff] [blame] | 2737 |     case Intrinsics::kMathMinFloatFloat: | 
| Aart Bik | 1f8d51b | 2018-02-15 10:42:37 -0800 | [diff] [blame] | 2738 |     case Intrinsics::kMathMinDoubleDouble: | 
| Aart Bik | 1f8d51b | 2018-02-15 10:42:37 -0800 | [diff] [blame] | 2739 |     case Intrinsics::kMathMaxIntInt: | 
| Aart Bik | 1f8d51b | 2018-02-15 10:42:37 -0800 | [diff] [blame] | 2740 |     case Intrinsics::kMathMaxLongLong: | 
| Aart Bik | 1f8d51b | 2018-02-15 10:42:37 -0800 | [diff] [blame] | 2741 |     case Intrinsics::kMathMaxFloatFloat: | 
| Aart Bik | 1f8d51b | 2018-02-15 10:42:37 -0800 | [diff] [blame] | 2742 |     case Intrinsics::kMathMaxDoubleDouble: | 
| Aart Bik | 3dad341 | 2018-02-28 12:01:46 -0800 | [diff] [blame] | 2743 |     case Intrinsics::kMathAbsInt: | 
| Aart Bik | 3dad341 | 2018-02-28 12:01:46 -0800 | [diff] [blame] | 2744 |     case Intrinsics::kMathAbsLong: | 
| Aart Bik | 3dad341 | 2018-02-28 12:01:46 -0800 | [diff] [blame] | 2745 |     case Intrinsics::kMathAbsFloat: | 
| Aart Bik | 3dad341 | 2018-02-28 12:01:46 -0800 | [diff] [blame] | 2746 |     case Intrinsics::kMathAbsDouble: | 
| Vladimir Marko | 5f84607 | 2020-04-09 13:20:11 +0100 | [diff] [blame] | 2747 |       // These are replaced by intermediate representation in the instruction builder. | 
 | 2748 |       LOG(FATAL) << "Unexpected " << static_cast<Intrinsics>(instruction->GetIntrinsic()); | 
 | 2749 |       UNREACHABLE(); | 
| Aart Bik | 2a6aad9 | 2016-02-25 11:32:32 -0800 | [diff] [blame] | 2750 |     default: | 
 | 2751 |       break; | 
| Nicolas Geoffray | a83a54d | 2015-10-02 17:30:26 +0100 | [diff] [blame] | 2752 |   } | 
 | 2753 | } | 
 | 2754 |  | 
| Aart Bik | bb245d1 | 2015-10-19 11:05:03 -0700 | [diff] [blame] | 2755 | void InstructionSimplifierVisitor::VisitDeoptimize(HDeoptimize* deoptimize) { | 
 | 2756 |   HInstruction* cond = deoptimize->InputAt(0); | 
 | 2757 |   if (cond->IsConstant()) { | 
| Roland Levillain | 1a65388 | 2016-03-18 18:05:57 +0000 | [diff] [blame] | 2758 |     if (cond->AsIntConstant()->IsFalse()) { | 
| Aart Bik | bb245d1 | 2015-10-19 11:05:03 -0700 | [diff] [blame] | 2759 |       // Never deopt: instruction can be removed. | 
| Nicolas Geoffray | 6f8e2c9 | 2017-03-23 14:37:26 +0000 | [diff] [blame] | 2760 |       if (deoptimize->GuardsAnInput()) { | 
 | 2761 |         deoptimize->ReplaceWith(deoptimize->GuardedInput()); | 
 | 2762 |       } | 
| Aart Bik | bb245d1 | 2015-10-19 11:05:03 -0700 | [diff] [blame] | 2763 |       deoptimize->GetBlock()->RemoveInstruction(deoptimize); | 
 | 2764 |     } else { | 
 | 2765 |       // Always deopt. | 
 | 2766 |     } | 
 | 2767 |   } | 
 | 2768 | } | 
 | 2769 |  | 
| Anton Kirilov | e14dc86 | 2016-05-13 17:56:15 +0100 | [diff] [blame] | 2770 | // Replace code looking like | 
 | 2771 | //    OP y, x, const1 | 
 | 2772 | //    OP z, y, const2 | 
 | 2773 | // with | 
 | 2774 | //    OP z, x, const3 | 
 | 2775 | // where OP is both an associative and a commutative operation. | 
 | 2776 | bool InstructionSimplifierVisitor::TryHandleAssociativeAndCommutativeOperation( | 
 | 2777 |     HBinaryOperation* instruction) { | 
 | 2778 |   DCHECK(instruction->IsCommutative()); | 
 | 2779 |  | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 2780 |   if (!DataType::IsIntegralType(instruction->GetType())) { | 
| Anton Kirilov | e14dc86 | 2016-05-13 17:56:15 +0100 | [diff] [blame] | 2781 |     return false; | 
 | 2782 |   } | 
 | 2783 |  | 
 | 2784 |   HInstruction* left = instruction->GetLeft(); | 
 | 2785 |   HInstruction* right = instruction->GetRight(); | 
 | 2786 |   // Variable names as described above. | 
 | 2787 |   HConstant* const2; | 
 | 2788 |   HBinaryOperation* y; | 
 | 2789 |  | 
| Vladimir Marko | 0dcccd8 | 2018-05-04 13:32:25 +0100 | [diff] [blame] | 2790 |   if (instruction->GetKind() == left->GetKind() && right->IsConstant()) { | 
| Anton Kirilov | e14dc86 | 2016-05-13 17:56:15 +0100 | [diff] [blame] | 2791 |     const2 = right->AsConstant(); | 
 | 2792 |     y = left->AsBinaryOperation(); | 
| Vladimir Marko | 0dcccd8 | 2018-05-04 13:32:25 +0100 | [diff] [blame] | 2793 |   } else if (left->IsConstant() && instruction->GetKind() == right->GetKind()) { | 
| Anton Kirilov | e14dc86 | 2016-05-13 17:56:15 +0100 | [diff] [blame] | 2794 |     const2 = left->AsConstant(); | 
 | 2795 |     y = right->AsBinaryOperation(); | 
 | 2796 |   } else { | 
 | 2797 |     // The node does not match the pattern. | 
 | 2798 |     return false; | 
 | 2799 |   } | 
 | 2800 |  | 
 | 2801 |   // If `y` has more than one use, we do not perform the optimization | 
 | 2802 |   // because it might increase code size (e.g. if the new constant is | 
 | 2803 |   // no longer encodable as an immediate operand in the target ISA). | 
 | 2804 |   if (!y->HasOnlyOneNonEnvironmentUse()) { | 
 | 2805 |     return false; | 
 | 2806 |   } | 
 | 2807 |  | 
 | 2808 |   // GetConstantRight() can return both left and right constants | 
 | 2809 |   // for commutative operations. | 
 | 2810 |   HConstant* const1 = y->GetConstantRight(); | 
 | 2811 |   if (const1 == nullptr) { | 
 | 2812 |     return false; | 
 | 2813 |   } | 
 | 2814 |  | 
 | 2815 |   instruction->ReplaceInput(const1, 0); | 
 | 2816 |   instruction->ReplaceInput(const2, 1); | 
 | 2817 |   HConstant* const3 = instruction->TryStaticEvaluation(); | 
 | 2818 |   DCHECK(const3 != nullptr); | 
 | 2819 |   instruction->ReplaceInput(y->GetLeastConstantLeft(), 0); | 
 | 2820 |   instruction->ReplaceInput(const3, 1); | 
 | 2821 |   RecordSimplification(); | 
 | 2822 |   return true; | 
 | 2823 | } | 
 | 2824 |  | 
 | 2825 | static HBinaryOperation* AsAddOrSub(HInstruction* binop) { | 
 | 2826 |   return (binop->IsAdd() || binop->IsSub()) ? binop->AsBinaryOperation() : nullptr; | 
 | 2827 | } | 
 | 2828 |  | 
 | 2829 | // Helper function that performs addition statically, considering the result type. | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 2830 | static int64_t ComputeAddition(DataType::Type type, int64_t x, int64_t y) { | 
| Anton Kirilov | e14dc86 | 2016-05-13 17:56:15 +0100 | [diff] [blame] | 2831 |   // Use the Compute() method for consistency with TryStaticEvaluation(). | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 2832 |   if (type == DataType::Type::kInt32) { | 
| Anton Kirilov | e14dc86 | 2016-05-13 17:56:15 +0100 | [diff] [blame] | 2833 |     return HAdd::Compute<int32_t>(x, y); | 
 | 2834 |   } else { | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 2835 |     DCHECK_EQ(type, DataType::Type::kInt64); | 
| Anton Kirilov | e14dc86 | 2016-05-13 17:56:15 +0100 | [diff] [blame] | 2836 |     return HAdd::Compute<int64_t>(x, y); | 
 | 2837 |   } | 
 | 2838 | } | 
 | 2839 |  | 
 | 2840 | // Helper function that handles the child classes of HConstant | 
 | 2841 | // and returns an integer with the appropriate sign. | 
 | 2842 | static int64_t GetValue(HConstant* constant, bool is_negated) { | 
 | 2843 |   int64_t ret = Int64FromConstant(constant); | 
 | 2844 |   return is_negated ? -ret : ret; | 
 | 2845 | } | 
 | 2846 |  | 
 | 2847 | // Replace code looking like | 
 | 2848 | //    OP1 y, x, const1 | 
 | 2849 | //    OP2 z, y, const2 | 
 | 2850 | // with | 
 | 2851 | //    OP3 z, x, const3 | 
 | 2852 | // where OPx is either ADD or SUB, and at least one of OP{1,2} is SUB. | 
 | 2853 | bool InstructionSimplifierVisitor::TrySubtractionChainSimplification( | 
 | 2854 |     HBinaryOperation* instruction) { | 
 | 2855 |   DCHECK(instruction->IsAdd() || instruction->IsSub()) << instruction->DebugName(); | 
 | 2856 |  | 
| Vladimir Marko | 0ebe0d8 | 2017-09-21 22:50:39 +0100 | [diff] [blame] | 2857 |   DataType::Type type = instruction->GetType(); | 
 | 2858 |   if (!DataType::IsIntegralType(type)) { | 
| Anton Kirilov | e14dc86 | 2016-05-13 17:56:15 +0100 | [diff] [blame] | 2859 |     return false; | 
 | 2860 |   } | 
 | 2861 |  | 
 | 2862 |   HInstruction* left = instruction->GetLeft(); | 
 | 2863 |   HInstruction* right = instruction->GetRight(); | 
 | 2864 |   // Variable names as described above. | 
 | 2865 |   HConstant* const2 = right->IsConstant() ? right->AsConstant() : left->AsConstant(); | 
 | 2866 |   if (const2 == nullptr) { | 
 | 2867 |     return false; | 
 | 2868 |   } | 
 | 2869 |  | 
 | 2870 |   HBinaryOperation* y = (AsAddOrSub(left) != nullptr) | 
 | 2871 |       ? left->AsBinaryOperation() | 
 | 2872 |       : AsAddOrSub(right); | 
 | 2873 |   // If y has more than one use, we do not perform the optimization because | 
 | 2874 |   // it might increase code size (e.g. if the new constant is no longer | 
 | 2875 |   // encodable as an immediate operand in the target ISA). | 
 | 2876 |   if ((y == nullptr) || !y->HasOnlyOneNonEnvironmentUse()) { | 
 | 2877 |     return false; | 
 | 2878 |   } | 
 | 2879 |  | 
 | 2880 |   left = y->GetLeft(); | 
 | 2881 |   HConstant* const1 = left->IsConstant() ? left->AsConstant() : y->GetRight()->AsConstant(); | 
 | 2882 |   if (const1 == nullptr) { | 
 | 2883 |     return false; | 
 | 2884 |   } | 
 | 2885 |  | 
 | 2886 |   HInstruction* x = (const1 == left) ? y->GetRight() : left; | 
 | 2887 |   // If both inputs are constants, let the constant folding pass deal with it. | 
 | 2888 |   if (x->IsConstant()) { | 
 | 2889 |     return false; | 
 | 2890 |   } | 
 | 2891 |  | 
 | 2892 |   bool is_const2_negated = (const2 == right) && instruction->IsSub(); | 
 | 2893 |   int64_t const2_val = GetValue(const2, is_const2_negated); | 
 | 2894 |   bool is_y_negated = (y == right) && instruction->IsSub(); | 
 | 2895 |   right = y->GetRight(); | 
 | 2896 |   bool is_const1_negated = is_y_negated ^ ((const1 == right) && y->IsSub()); | 
 | 2897 |   int64_t const1_val = GetValue(const1, is_const1_negated); | 
 | 2898 |   bool is_x_negated = is_y_negated ^ ((x == right) && y->IsSub()); | 
 | 2899 |   int64_t const3_val = ComputeAddition(type, const1_val, const2_val); | 
 | 2900 |   HBasicBlock* block = instruction->GetBlock(); | 
 | 2901 |   HConstant* const3 = block->GetGraph()->GetConstant(type, const3_val); | 
| Vladimir Marko | e764d2e | 2017-10-05 14:35:55 +0100 | [diff] [blame] | 2902 |   ArenaAllocator* allocator = instruction->GetAllocator(); | 
| Anton Kirilov | e14dc86 | 2016-05-13 17:56:15 +0100 | [diff] [blame] | 2903 |   HInstruction* z; | 
 | 2904 |  | 
 | 2905 |   if (is_x_negated) { | 
| Vladimir Marko | e764d2e | 2017-10-05 14:35:55 +0100 | [diff] [blame] | 2906 |     z = new (allocator) HSub(type, const3, x, instruction->GetDexPc()); | 
| Anton Kirilov | e14dc86 | 2016-05-13 17:56:15 +0100 | [diff] [blame] | 2907 |   } else { | 
| Vladimir Marko | e764d2e | 2017-10-05 14:35:55 +0100 | [diff] [blame] | 2908 |     z = new (allocator) HAdd(type, x, const3, instruction->GetDexPc()); | 
| Anton Kirilov | e14dc86 | 2016-05-13 17:56:15 +0100 | [diff] [blame] | 2909 |   } | 
 | 2910 |  | 
 | 2911 |   block->ReplaceAndRemoveInstructionWith(instruction, z); | 
 | 2912 |   RecordSimplification(); | 
 | 2913 |   return true; | 
 | 2914 | } | 
 | 2915 |  | 
| Lena Djokic | bc5460b | 2017-07-20 16:07:36 +0200 | [diff] [blame] | 2916 | void InstructionSimplifierVisitor::VisitVecMul(HVecMul* instruction) { | 
 | 2917 |   if (TryCombineVecMultiplyAccumulate(instruction)) { | 
 | 2918 |     RecordSimplification(); | 
 | 2919 |   } | 
 | 2920 | } | 
 | 2921 |  | 
| Nicolas Geoffray | 3c04974 | 2014-09-24 18:10:46 +0100 | [diff] [blame] | 2922 | }  // namespace art |