instruction_simplifier.cc revision 339dfc209ad93482269eea1386e79973abc313cf
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
19#include "mirror/class-inl.h"
20#include "scoped_thread_state_change.h"
21
22namespace art {
23
24class InstructionSimplifierVisitor : public HGraphVisitor {
25 public:
26  InstructionSimplifierVisitor(HGraph* graph, OptimizingCompilerStats* stats)
27      : HGraphVisitor(graph),
28        stats_(stats) {}
29
30  void Run();
31
32 private:
33  void RecordSimplification() {
34    simplification_occurred_ = true;
35    simplifications_at_current_position_++;
36    if (stats_) {
37      stats_->RecordStat(kInstructionSimplifications);
38    }
39  }
40
41  bool TryMoveNegOnInputsAfterBinop(HBinaryOperation* binop);
42  void VisitShift(HBinaryOperation* shift);
43
44  void VisitSuspendCheck(HSuspendCheck* check) OVERRIDE;
45  void VisitEqual(HEqual* equal) OVERRIDE;
46  void VisitArraySet(HArraySet* equal) OVERRIDE;
47  void VisitTypeConversion(HTypeConversion* instruction) OVERRIDE;
48  void VisitNullCheck(HNullCheck* instruction) OVERRIDE;
49  void VisitArrayLength(HArrayLength* instruction) OVERRIDE;
50  void VisitCheckCast(HCheckCast* instruction) OVERRIDE;
51  void VisitAdd(HAdd* instruction) OVERRIDE;
52  void VisitAnd(HAnd* instruction) OVERRIDE;
53  void VisitDiv(HDiv* instruction) OVERRIDE;
54  void VisitMul(HMul* instruction) OVERRIDE;
55  void VisitNeg(HNeg* instruction) OVERRIDE;
56  void VisitNot(HNot* instruction) OVERRIDE;
57  void VisitOr(HOr* instruction) OVERRIDE;
58  void VisitShl(HShl* instruction) OVERRIDE;
59  void VisitShr(HShr* instruction) OVERRIDE;
60  void VisitSub(HSub* instruction) OVERRIDE;
61  void VisitUShr(HUShr* instruction) OVERRIDE;
62  void VisitXor(HXor* instruction) OVERRIDE;
63
64  OptimizingCompilerStats* stats_;
65  bool simplification_occurred_ = false;
66  int simplifications_at_current_position_ = 0;
67  // We ensure we do not loop infinitely. The value is a finger in the air guess
68  // that should allow enough simplification.
69  static constexpr int kMaxSamePositionSimplifications = 10;
70};
71
72void InstructionSimplifier::Run() {
73  InstructionSimplifierVisitor visitor(graph_, stats_);
74  visitor.Run();
75}
76
77void InstructionSimplifierVisitor::Run() {
78  for (HReversePostOrderIterator it(*GetGraph()); !it.Done();) {
79    // The simplification of an instruction to another instruction may yield
80    // possibilities for other simplifications. So although we perform a reverse
81    // post order visit, we sometimes need to revisit an instruction index.
82    simplification_occurred_ = false;
83    VisitBasicBlock(it.Current());
84    if (simplification_occurred_ &&
85        (simplifications_at_current_position_ < kMaxSamePositionSimplifications)) {
86      // New simplifications may be applicable to the instruction at the
87      // current index, so don't advance the iterator.
88      continue;
89    }
90    if (simplifications_at_current_position_ >= kMaxSamePositionSimplifications) {
91      LOG(WARNING) << "Too many simplifications (" << simplifications_at_current_position_
92          << ") occurred at the current position.";
93    }
94    simplifications_at_current_position_ = 0;
95    it.Advance();
96  }
97}
98
99namespace {
100
101bool AreAllBitsSet(HConstant* constant) {
102  return Int64FromConstant(constant) == -1;
103}
104
105}  // namespace
106
107// Returns true if the code was simplified to use only one negation operation
108// after the binary operation instead of one on each of the inputs.
109bool InstructionSimplifierVisitor::TryMoveNegOnInputsAfterBinop(HBinaryOperation* binop) {
110  DCHECK(binop->IsAdd() || binop->IsSub());
111  DCHECK(binop->GetLeft()->IsNeg() && binop->GetRight()->IsNeg());
112  HNeg* left_neg = binop->GetLeft()->AsNeg();
113  HNeg* right_neg = binop->GetRight()->AsNeg();
114  if (!left_neg->HasOnlyOneNonEnvironmentUse() ||
115      !right_neg->HasOnlyOneNonEnvironmentUse()) {
116    return false;
117  }
118  // Replace code looking like
119  //    NEG tmp1, a
120  //    NEG tmp2, b
121  //    ADD dst, tmp1, tmp2
122  // with
123  //    ADD tmp, a, b
124  //    NEG dst, tmp
125  binop->ReplaceInput(left_neg->GetInput(), 0);
126  binop->ReplaceInput(right_neg->GetInput(), 1);
127  left_neg->GetBlock()->RemoveInstruction(left_neg);
128  right_neg->GetBlock()->RemoveInstruction(right_neg);
129  HNeg* neg = new (GetGraph()->GetArena()) HNeg(binop->GetType(), binop);
130  binop->GetBlock()->InsertInstructionBefore(neg, binop->GetNext());
131  binop->ReplaceWithExceptInReplacementAtIndex(neg, 0);
132  RecordSimplification();
133  return true;
134}
135
136void InstructionSimplifierVisitor::VisitShift(HBinaryOperation* instruction) {
137  DCHECK(instruction->IsShl() || instruction->IsShr() || instruction->IsUShr());
138  HConstant* input_cst = instruction->GetConstantRight();
139  HInstruction* input_other = instruction->GetLeastConstantLeft();
140
141  if ((input_cst != nullptr) && input_cst->IsZero()) {
142    // Replace code looking like
143    //    SHL dst, src, 0
144    // with
145    //    src
146    instruction->ReplaceWith(input_other);
147    instruction->GetBlock()->RemoveInstruction(instruction);
148  }
149}
150
151void InstructionSimplifierVisitor::VisitNullCheck(HNullCheck* null_check) {
152  HInstruction* obj = null_check->InputAt(0);
153  if (!obj->CanBeNull()) {
154    null_check->ReplaceWith(obj);
155    null_check->GetBlock()->RemoveInstruction(null_check);
156    if (stats_ != nullptr) {
157      stats_->RecordStat(MethodCompilationStat::kRemovedNullCheck);
158    }
159  }
160}
161
162void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) {
163  HLoadClass* load_class = check_cast->InputAt(1)->AsLoadClass();
164  if (!load_class->IsResolved()) {
165    // If the class couldn't be resolve it's not safe to compare against it. It's
166    // default type would be Top which might be wider that the actual class type
167    // and thus producing wrong results.
168    return;
169  }
170  ReferenceTypeInfo obj_rti = check_cast->InputAt(0)->GetReferenceTypeInfo();
171  ReferenceTypeInfo class_rti = load_class->GetLoadedClassRTI();
172  ScopedObjectAccess soa(Thread::Current());
173  if (class_rti.IsSupertypeOf(obj_rti)) {
174    check_cast->GetBlock()->RemoveInstruction(check_cast);
175    if (stats_ != nullptr) {
176      stats_->RecordStat(MethodCompilationStat::kRemovedCheckedCast);
177    }
178  }
179}
180
181void InstructionSimplifierVisitor::VisitSuspendCheck(HSuspendCheck* check) {
182  HBasicBlock* block = check->GetBlock();
183  // Currently always keep the suspend check at entry.
184  if (block->IsEntryBlock()) return;
185
186  // Currently always keep suspend checks at loop entry.
187  if (block->IsLoopHeader() && block->GetFirstInstruction() == check) {
188    DCHECK(block->GetLoopInformation()->GetSuspendCheck() == check);
189    return;
190  }
191
192  // Remove the suspend check that was added at build time for the baseline
193  // compiler.
194  block->RemoveInstruction(check);
195}
196
197void InstructionSimplifierVisitor::VisitEqual(HEqual* equal) {
198  HInstruction* input1 = equal->InputAt(0);
199  HInstruction* input2 = equal->InputAt(1);
200  if (input1->GetType() == Primitive::kPrimBoolean && input2->IsIntConstant()) {
201    if (input2->AsIntConstant()->GetValue() == 1) {
202      // Replace (bool_value == 1) with bool_value
203      equal->ReplaceWith(equal->InputAt(0));
204      equal->GetBlock()->RemoveInstruction(equal);
205    } else {
206      // We should replace (bool_value == 0) with !bool_value, but we unfortunately
207      // do not have such instruction.
208      DCHECK_EQ(input2->AsIntConstant()->GetValue(), 0);
209    }
210  }
211}
212
213void InstructionSimplifierVisitor::VisitArrayLength(HArrayLength* instruction) {
214  HInstruction* input = instruction->InputAt(0);
215  // If the array is a NewArray with constant size, replace the array length
216  // with the constant instruction. This helps the bounds check elimination phase.
217  if (input->IsNewArray()) {
218    input = input->InputAt(0);
219    if (input->IsIntConstant()) {
220      instruction->ReplaceWith(input);
221    }
222  }
223}
224
225void InstructionSimplifierVisitor::VisitArraySet(HArraySet* instruction) {
226  HInstruction* value = instruction->GetValue();
227  if (value->GetType() != Primitive::kPrimNot) return;
228
229  if (value->IsArrayGet()) {
230    if (value->AsArrayGet()->GetArray() == instruction->GetArray()) {
231      // If the code is just swapping elements in the array, no need for a type check.
232      instruction->ClearNeedsTypeCheck();
233    }
234  }
235}
236
237void InstructionSimplifierVisitor::VisitTypeConversion(HTypeConversion* instruction) {
238  if (instruction->GetResultType() == instruction->GetInputType()) {
239    // Remove the instruction if it's converting to the same type.
240    instruction->ReplaceWith(instruction->GetInput());
241    instruction->GetBlock()->RemoveInstruction(instruction);
242  }
243}
244
245void InstructionSimplifierVisitor::VisitAdd(HAdd* instruction) {
246  HConstant* input_cst = instruction->GetConstantRight();
247  HInstruction* input_other = instruction->GetLeastConstantLeft();
248  if ((input_cst != nullptr) && input_cst->IsZero()) {
249    // Replace code looking like
250    //    ADD dst, src, 0
251    // with
252    //    src
253    instruction->ReplaceWith(input_other);
254    instruction->GetBlock()->RemoveInstruction(instruction);
255    return;
256  }
257
258  HInstruction* left = instruction->GetLeft();
259  HInstruction* right = instruction->GetRight();
260  bool left_is_neg = left->IsNeg();
261  bool right_is_neg = right->IsNeg();
262
263  if (left_is_neg && right_is_neg) {
264    if (TryMoveNegOnInputsAfterBinop(instruction)) {
265      return;
266    }
267  }
268
269  HNeg* neg = left_is_neg ? left->AsNeg() : right->AsNeg();
270  if ((left_is_neg ^ right_is_neg) && neg->HasOnlyOneNonEnvironmentUse()) {
271    // Replace code looking like
272    //    NEG tmp, b
273    //    ADD dst, a, tmp
274    // with
275    //    SUB dst, a, b
276    // We do not perform the optimization if the input negation has environment
277    // uses or multiple non-environment uses as it could lead to worse code. In
278    // particular, we do not want the live range of `b` to be extended if we are
279    // not sure the initial 'NEG' instruction can be removed.
280    HInstruction* other = left_is_neg ? right : left;
281    HSub* sub = new(GetGraph()->GetArena()) HSub(instruction->GetType(), other, neg->GetInput());
282    instruction->GetBlock()->ReplaceAndRemoveInstructionWith(instruction, sub);
283    RecordSimplification();
284    neg->GetBlock()->RemoveInstruction(neg);
285  }
286}
287
288void InstructionSimplifierVisitor::VisitAnd(HAnd* instruction) {
289  HConstant* input_cst = instruction->GetConstantRight();
290  HInstruction* input_other = instruction->GetLeastConstantLeft();
291
292  if ((input_cst != nullptr) && AreAllBitsSet(input_cst)) {
293    // Replace code looking like
294    //    AND dst, src, 0xFFF...FF
295    // with
296    //    src
297    instruction->ReplaceWith(input_other);
298    instruction->GetBlock()->RemoveInstruction(instruction);
299    return;
300  }
301
302  // We assume that GVN has run before, so we only perform a pointer comparison.
303  // If for some reason the values are equal but the pointers are different, we
304  // are still correct and only miss an optimization opportunity.
305  if (instruction->GetLeft() == instruction->GetRight()) {
306    // Replace code looking like
307    //    AND dst, src, src
308    // with
309    //    src
310    instruction->ReplaceWith(instruction->GetLeft());
311    instruction->GetBlock()->RemoveInstruction(instruction);
312  }
313}
314
315void InstructionSimplifierVisitor::VisitDiv(HDiv* instruction) {
316  HConstant* input_cst = instruction->GetConstantRight();
317  HInstruction* input_other = instruction->GetLeastConstantLeft();
318  Primitive::Type type = instruction->GetType();
319
320  if ((input_cst != nullptr) && input_cst->IsOne()) {
321    // Replace code looking like
322    //    DIV dst, src, 1
323    // with
324    //    src
325    instruction->ReplaceWith(input_other);
326    instruction->GetBlock()->RemoveInstruction(instruction);
327    return;
328  }
329
330  if ((input_cst != nullptr) && input_cst->IsMinusOne() &&
331      (Primitive::IsFloatingPointType(type) || Primitive::IsIntOrLongType(type))) {
332    // Replace code looking like
333    //    DIV dst, src, -1
334    // with
335    //    NEG dst, src
336    instruction->GetBlock()->ReplaceAndRemoveInstructionWith(
337        instruction, (new (GetGraph()->GetArena()) HNeg(type, input_other)));
338    RecordSimplification();
339  }
340}
341
342void InstructionSimplifierVisitor::VisitMul(HMul* instruction) {
343  HConstant* input_cst = instruction->GetConstantRight();
344  HInstruction* input_other = instruction->GetLeastConstantLeft();
345  Primitive::Type type = instruction->GetType();
346  HBasicBlock* block = instruction->GetBlock();
347  ArenaAllocator* allocator = GetGraph()->GetArena();
348
349  if (input_cst == nullptr) {
350    return;
351  }
352
353  if (input_cst->IsOne()) {
354    // Replace code looking like
355    //    MUL dst, src, 1
356    // with
357    //    src
358    instruction->ReplaceWith(input_other);
359    instruction->GetBlock()->RemoveInstruction(instruction);
360    return;
361  }
362
363  if (input_cst->IsMinusOne() &&
364      (Primitive::IsFloatingPointType(type) || Primitive::IsIntOrLongType(type))) {
365    // Replace code looking like
366    //    MUL dst, src, -1
367    // with
368    //    NEG dst, src
369    HNeg* neg = new (allocator) HNeg(type, input_other);
370    block->ReplaceAndRemoveInstructionWith(instruction, neg);
371    RecordSimplification();
372    return;
373  }
374
375  if (Primitive::IsFloatingPointType(type) &&
376      ((input_cst->IsFloatConstant() && input_cst->AsFloatConstant()->GetValue() == 2.0f) ||
377       (input_cst->IsDoubleConstant() && input_cst->AsDoubleConstant()->GetValue() == 2.0))) {
378    // Replace code looking like
379    //    FP_MUL dst, src, 2.0
380    // with
381    //    FP_ADD dst, src, src
382    // The 'int' and 'long' cases are handled below.
383    block->ReplaceAndRemoveInstructionWith(instruction,
384                                           new (allocator) HAdd(type, input_other, input_other));
385    RecordSimplification();
386    return;
387  }
388
389  if (Primitive::IsIntOrLongType(type)) {
390    int64_t factor = Int64FromConstant(input_cst);
391    // We expect the `0` case to have been handled in the constant folding pass.
392    DCHECK_NE(factor, 0);
393    if (IsPowerOfTwo(factor)) {
394      // Replace code looking like
395      //    MUL dst, src, pow_of_2
396      // with
397      //    SHL dst, src, log2(pow_of_2)
398      HIntConstant* shift = GetGraph()->GetIntConstant(WhichPowerOf2(factor));
399      HShl* shl = new(allocator) HShl(type, input_other, shift);
400      block->ReplaceAndRemoveInstructionWith(instruction, shl);
401      RecordSimplification();
402    }
403  }
404}
405
406void InstructionSimplifierVisitor::VisitNeg(HNeg* instruction) {
407  HInstruction* input = instruction->GetInput();
408  if (input->IsNeg()) {
409    // Replace code looking like
410    //    NEG tmp, src
411    //    NEG dst, tmp
412    // with
413    //    src
414    HNeg* previous_neg = input->AsNeg();
415    instruction->ReplaceWith(previous_neg->GetInput());
416    instruction->GetBlock()->RemoveInstruction(instruction);
417    // We perform the optimization even if the input negation has environment
418    // uses since it allows removing the current instruction. But we only delete
419    // the input negation only if it is does not have any uses left.
420    if (!previous_neg->HasUses()) {
421      previous_neg->GetBlock()->RemoveInstruction(previous_neg);
422    }
423    RecordSimplification();
424    return;
425  }
426
427  if (input->IsSub() && input->HasOnlyOneNonEnvironmentUse() &&
428      !Primitive::IsFloatingPointType(input->GetType())) {
429    // Replace code looking like
430    //    SUB tmp, a, b
431    //    NEG dst, tmp
432    // with
433    //    SUB dst, b, a
434    // We do not perform the optimization if the input subtraction has
435    // environment uses or multiple non-environment uses as it could lead to
436    // worse code. In particular, we do not want the live ranges of `a` and `b`
437    // to be extended if we are not sure the initial 'SUB' instruction can be
438    // removed.
439    // We do not perform optimization for fp because we could lose the sign of zero.
440    HSub* sub = input->AsSub();
441    HSub* new_sub =
442        new (GetGraph()->GetArena()) HSub(instruction->GetType(), sub->GetRight(), sub->GetLeft());
443    instruction->GetBlock()->ReplaceAndRemoveInstructionWith(instruction, new_sub);
444    if (!sub->HasUses()) {
445      sub->GetBlock()->RemoveInstruction(sub);
446    }
447    RecordSimplification();
448  }
449}
450
451void InstructionSimplifierVisitor::VisitNot(HNot* instruction) {
452  HInstruction* input = instruction->GetInput();
453  if (input->IsNot()) {
454    // Replace code looking like
455    //    NOT tmp, src
456    //    NOT dst, tmp
457    // with
458    //    src
459    // We perform the optimization even if the input negation has environment
460    // uses since it allows removing the current instruction. But we only delete
461    // the input negation only if it is does not have any uses left.
462    HNot* previous_not = input->AsNot();
463    instruction->ReplaceWith(previous_not->GetInput());
464    instruction->GetBlock()->RemoveInstruction(instruction);
465    if (!previous_not->HasUses()) {
466      previous_not->GetBlock()->RemoveInstruction(previous_not);
467    }
468    RecordSimplification();
469  }
470}
471
472void InstructionSimplifierVisitor::VisitOr(HOr* instruction) {
473  HConstant* input_cst = instruction->GetConstantRight();
474  HInstruction* input_other = instruction->GetLeastConstantLeft();
475
476  if ((input_cst != nullptr) && input_cst->IsZero()) {
477    // Replace code looking like
478    //    OR dst, src, 0
479    // with
480    //    src
481    instruction->ReplaceWith(input_other);
482    instruction->GetBlock()->RemoveInstruction(instruction);
483    return;
484  }
485
486  // We assume that GVN has run before, so we only perform a pointer comparison.
487  // If for some reason the values are equal but the pointers are different, we
488  // are still correct and only miss an optimization opportunity.
489  if (instruction->GetLeft() == instruction->GetRight()) {
490    // Replace code looking like
491    //    OR dst, src, src
492    // with
493    //    src
494    instruction->ReplaceWith(instruction->GetLeft());
495    instruction->GetBlock()->RemoveInstruction(instruction);
496  }
497}
498
499void InstructionSimplifierVisitor::VisitShl(HShl* instruction) {
500  VisitShift(instruction);
501}
502
503void InstructionSimplifierVisitor::VisitShr(HShr* instruction) {
504  VisitShift(instruction);
505}
506
507void InstructionSimplifierVisitor::VisitSub(HSub* instruction) {
508  HConstant* input_cst = instruction->GetConstantRight();
509  HInstruction* input_other = instruction->GetLeastConstantLeft();
510
511  if ((input_cst != nullptr) && input_cst->IsZero()) {
512    // Replace code looking like
513    //    SUB dst, src, 0
514    // with
515    //    src
516    instruction->ReplaceWith(input_other);
517    instruction->GetBlock()->RemoveInstruction(instruction);
518    return;
519  }
520
521  Primitive::Type type = instruction->GetType();
522  if (!Primitive::IsIntegralType(type)) {
523    return;
524  }
525
526  HBasicBlock* block = instruction->GetBlock();
527  ArenaAllocator* allocator = GetGraph()->GetArena();
528
529  HInstruction* left = instruction->GetLeft();
530  HInstruction* right = instruction->GetRight();
531  if (left->IsConstant()) {
532    if (Int64FromConstant(left->AsConstant()) == 0) {
533      // Replace code looking like
534      //    SUB dst, 0, src
535      // with
536      //    NEG dst, src
537      // Note that we cannot optimize `0.0 - x` to `-x` for floating-point. When
538      // `x` is `0.0`, the former expression yields `0.0`, while the later
539      // yields `-0.0`.
540      HNeg* neg = new (allocator) HNeg(type, right);
541      block->ReplaceAndRemoveInstructionWith(instruction, neg);
542      RecordSimplification();
543      return;
544    }
545  }
546
547  if (left->IsNeg() && right->IsNeg()) {
548    if (TryMoveNegOnInputsAfterBinop(instruction)) {
549      return;
550    }
551  }
552
553  if (right->IsNeg() && right->HasOnlyOneNonEnvironmentUse()) {
554    // Replace code looking like
555    //    NEG tmp, b
556    //    SUB dst, a, tmp
557    // with
558    //    ADD dst, a, b
559    HAdd* add = new(GetGraph()->GetArena()) HAdd(type, left, right->AsNeg()->GetInput());
560    instruction->GetBlock()->ReplaceAndRemoveInstructionWith(instruction, add);
561    RecordSimplification();
562    right->GetBlock()->RemoveInstruction(right);
563    return;
564  }
565
566  if (left->IsNeg() && left->HasOnlyOneNonEnvironmentUse()) {
567    // Replace code looking like
568    //    NEG tmp, a
569    //    SUB dst, tmp, b
570    // with
571    //    ADD tmp, a, b
572    //    NEG dst, tmp
573    // The second version is not intrinsically better, but enables more
574    // transformations.
575    HAdd* add = new(GetGraph()->GetArena()) HAdd(type, left->AsNeg()->GetInput(), right);
576    instruction->GetBlock()->InsertInstructionBefore(add, instruction);
577    HNeg* neg = new (GetGraph()->GetArena()) HNeg(instruction->GetType(), add);
578    instruction->GetBlock()->InsertInstructionBefore(neg, instruction);
579    instruction->ReplaceWith(neg);
580    instruction->GetBlock()->RemoveInstruction(instruction);
581    RecordSimplification();
582    left->GetBlock()->RemoveInstruction(left);
583  }
584}
585
586void InstructionSimplifierVisitor::VisitUShr(HUShr* instruction) {
587  VisitShift(instruction);
588}
589
590void InstructionSimplifierVisitor::VisitXor(HXor* instruction) {
591  HConstant* input_cst = instruction->GetConstantRight();
592  HInstruction* input_other = instruction->GetLeastConstantLeft();
593
594  if ((input_cst != nullptr) && input_cst->IsZero()) {
595    // Replace code looking like
596    //    XOR dst, src, 0
597    // with
598    //    src
599    instruction->ReplaceWith(input_other);
600    instruction->GetBlock()->RemoveInstruction(instruction);
601    return;
602  }
603
604  if ((input_cst != nullptr) && AreAllBitsSet(input_cst)) {
605    // Replace code looking like
606    //    XOR dst, src, 0xFFF...FF
607    // with
608    //    NOT dst, src
609    HNot* bitwise_not = new (GetGraph()->GetArena()) HNot(instruction->GetType(), input_other);
610    instruction->GetBlock()->ReplaceAndRemoveInstructionWith(instruction, bitwise_not);
611    RecordSimplification();
612    return;
613  }
614}
615
616}  // namespace art
617