• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4 
5 package com.android.tools.r8.ir.optimize;
6 
7 import com.android.tools.r8.dex.Constants;
8 import com.android.tools.r8.errors.CompilationError;
9 import com.android.tools.r8.graph.AppInfo;
10 import com.android.tools.r8.graph.DexClass;
11 import com.android.tools.r8.graph.DexEncodedMethod;
12 import com.android.tools.r8.graph.DexField;
13 import com.android.tools.r8.graph.DexItemFactory;
14 import com.android.tools.r8.graph.DexMethod;
15 import com.android.tools.r8.graph.DexProto;
16 import com.android.tools.r8.graph.DexType;
17 import com.android.tools.r8.ir.code.ArrayGet;
18 import com.android.tools.r8.ir.code.ArrayPut;
19 import com.android.tools.r8.ir.code.BasicBlock;
20 import com.android.tools.r8.ir.code.Binop;
21 import com.android.tools.r8.ir.code.CatchHandlers;
22 import com.android.tools.r8.ir.code.Cmp;
23 import com.android.tools.r8.ir.code.Cmp.Bias;
24 import com.android.tools.r8.ir.code.ConstNumber;
25 import com.android.tools.r8.ir.code.ConstString;
26 import com.android.tools.r8.ir.code.DominatorTree;
27 import com.android.tools.r8.ir.code.Goto;
28 import com.android.tools.r8.ir.code.IRCode;
29 import com.android.tools.r8.ir.code.If;
30 import com.android.tools.r8.ir.code.If.Type;
31 import com.android.tools.r8.ir.code.Instruction;
32 import com.android.tools.r8.ir.code.InstructionIterator;
33 import com.android.tools.r8.ir.code.InstructionListIterator;
34 import com.android.tools.r8.ir.code.Invoke;
35 import com.android.tools.r8.ir.code.InvokeDirect;
36 import com.android.tools.r8.ir.code.InvokeMethod;
37 import com.android.tools.r8.ir.code.InvokeVirtual;
38 import com.android.tools.r8.ir.code.MemberType;
39 import com.android.tools.r8.ir.code.MoveType;
40 import com.android.tools.r8.ir.code.NewArrayEmpty;
41 import com.android.tools.r8.ir.code.NewArrayFilledData;
42 import com.android.tools.r8.ir.code.NumericType;
43 import com.android.tools.r8.ir.code.Phi;
44 import com.android.tools.r8.ir.code.Return;
45 import com.android.tools.r8.ir.code.StaticGet;
46 import com.android.tools.r8.ir.code.StaticPut;
47 import com.android.tools.r8.ir.code.Switch;
48 import com.android.tools.r8.ir.code.Value;
49 import com.android.tools.r8.ir.conversion.OptimizationFeedback;
50 import com.android.tools.r8.utils.InternalOptions;
51 import com.android.tools.r8.utils.LongInterval;
52 import com.google.common.base.Equivalence;
53 import com.google.common.base.Equivalence.Wrapper;
54 import com.google.common.collect.ArrayListMultimap;
55 import com.google.common.collect.ImmutableList;
56 import com.google.common.collect.ListMultimap;
57 import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
58 import it.unimi.dsi.fastutil.ints.Int2IntMap;
59 import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
60 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
61 import it.unimi.dsi.fastutil.ints.IntArrayList;
62 import it.unimi.dsi.fastutil.ints.IntList;
63 import it.unimi.dsi.fastutil.objects.Reference2IntArrayMap;
64 import it.unimi.dsi.fastutil.objects.Reference2IntMap;
65 import java.util.ArrayList;
66 import java.util.Comparator;
67 import java.util.HashMap;
68 import java.util.HashSet;
69 import java.util.IdentityHashMap;
70 import java.util.Iterator;
71 import java.util.LinkedList;
72 import java.util.List;
73 import java.util.ListIterator;
74 import java.util.Map;
75 import java.util.Queue;
76 import java.util.Set;
77 
78 public class CodeRewriter {
79 
80   private static final int UNKNOWN_CAN_THROW = 0;
81   private static final int CAN_THROW = 1;
82   private static final int CANNOT_THROW = 2;
83   private static final int MAX_FILL_ARRAY_SIZE = 8 * Constants.KILOBYTE;
84   // This constant was determined by experimentation.
85   private static final int STOP_SHARED_CONSTANT_THRESHOLD = 50;
86 
87   private final AppInfo appInfo;
88   private final DexItemFactory dexItemFactory;
89   private final Set<DexType> libraryClassesWithOptimizationInfo;
90 
CodeRewriter(AppInfo appInfo, Set<DexType> libraryClassesWithOptimizationInfo)91   public CodeRewriter(AppInfo appInfo, Set<DexType> libraryClassesWithOptimizationInfo) {
92     this.appInfo = appInfo;
93     this.dexItemFactory = appInfo.dexItemFactory;
94     this.libraryClassesWithOptimizationInfo = libraryClassesWithOptimizationInfo;
95   }
96 
97   /**
98    * Removes all debug positions that are not needed to maintain proper stack trace information.
99    * If a debug position is followed by another debug position and no instructions between the two
100    * can throw then it is unneeded (in a release build).
101    * If a block with a position has (normal) outgoing edges, this property depends on the
102    * possibility of the successors throwing before the next debug position is hit.
103    */
removedUnneededDebugPositions(IRCode code)104   public static boolean removedUnneededDebugPositions(IRCode code) {
105     computeThrowsColorForAllBlocks(code);
106     for (BasicBlock block : code.blocks) {
107       InstructionListIterator iterator = block.listIterator();
108       while (iterator.hasNext()) {
109         Instruction instruction = iterator.next();
110         if (instruction.isDebugPosition()
111             && getThrowsColorForBlock(block, iterator.nextIndex()) == CANNOT_THROW) {
112           iterator.remove();
113         }
114       }
115     }
116     return true;
117   }
118 
computeThrowsColorForAllBlocks(IRCode code)119   private static void computeThrowsColorForAllBlocks(IRCode code) {
120     // First pass colors blocks in reverse topological order, based on the instructions.
121     code.clearMarks();
122     List<BasicBlock> blocks = code.blocks;
123     ArrayList<BasicBlock> worklist = new ArrayList<>();
124     for (int i = blocks.size() - 1; i >= 0; i--) {
125       BasicBlock block = blocks.get(i);
126       // Mark the block as not-throwing if no successor implies otherwise.
127       // This ensures that a loop back to this block will be seen as non-throwing.
128       block.setColor(CANNOT_THROW);
129       int color = getThrowsColorForBlock(block, 0);
130       block.setColor(color);
131       if (color == UNKNOWN_CAN_THROW) {
132         worklist.add(block);
133       }
134     }
135     // A fixed point then ensures that we propagate the color backwards over normal edges.
136     ArrayList<BasicBlock> remaining = new ArrayList<>(worklist.size());
137     while (!worklist.isEmpty()) {
138       ImmutableList<BasicBlock> work = new ImmutableList.Builder<BasicBlock>()
139           .addAll(worklist)
140           .addAll(remaining)
141           .build();
142       worklist.clear();
143       remaining.clear();
144       for (BasicBlock block : work) {
145         if (!block.hasColor(UNKNOWN_CAN_THROW)) {
146           continue;
147         }
148         block.setColor(CANNOT_THROW);
149         int color = getThrowsColorForSuccessors(block);
150         block.setColor(color);
151         if (color == UNKNOWN_CAN_THROW) {
152           remaining.add(block);
153         } else {
154           for (BasicBlock predecessor : block.getNormalPredecessors()) {
155             if (predecessor.hasColor(UNKNOWN_CAN_THROW)) {
156               worklist.add(predecessor);
157             }
158           }
159         }
160       }
161     }
162     // Any remaining set of blocks represents a cycle of blocks containing no throwing instructions.
163     for (BasicBlock block : remaining) {
164       assert !block.canThrow();
165       block.setColor(CANNOT_THROW);
166     }
167   }
168 
getThrowsColorForBlock(BasicBlock block, int index)169   private static int getThrowsColorForBlock(BasicBlock block, int index) {
170     InstructionListIterator iterator = block.listIterator(index);
171     while (iterator.hasNext()) {
172       Instruction instruction = iterator.next();
173       if (instruction.isDebugPosition()) {
174         return CANNOT_THROW;
175       }
176       if (instruction.instructionTypeCanThrow()) {
177         return CAN_THROW;
178       }
179     }
180     return getThrowsColorForSuccessors(block);
181   }
182 
getThrowsColorForSuccessors(BasicBlock block)183   private static int getThrowsColorForSuccessors(BasicBlock block) {
184     int color = CANNOT_THROW;
185     for (BasicBlock successor : block.getNormalSucessors()) {
186       if (successor.hasColor(CAN_THROW)) {
187         return CAN_THROW;
188       }
189       if (successor.hasColor(UNKNOWN_CAN_THROW)) {
190         color = UNKNOWN_CAN_THROW;
191       }
192     }
193     return color;
194   }
195 
removedTrivialGotos(IRCode code)196   private static boolean removedTrivialGotos(IRCode code) {
197     ListIterator<BasicBlock> iterator = code.listIterator();
198     assert iterator.hasNext();
199     BasicBlock block = iterator.next();
200     BasicBlock nextBlock;
201     do {
202       nextBlock = iterator.hasNext() ? iterator.next() : null;
203       // Trivial goto block are only kept if they are self-targeting or are targeted by
204       // fallthroughs.
205       BasicBlock blk = block;  // Additional local for lambda below.
206       assert !block.isTrivialGoto()
207           || block.exit().asGoto().getTarget() == block
208           || block.getPredecessors().stream().anyMatch((b) -> b.exit().fallthroughBlock() == blk);
209       // Trivial goto blocks never target the next block (in that case there should just be a
210       // fallthrough).
211       assert !block.isTrivialGoto() || block.exit().asGoto().getTarget() != nextBlock;
212       block = nextBlock;
213     } while (block != null);
214     return true;
215   }
216 
endOfGotoChain(BasicBlock block)217   private static BasicBlock endOfGotoChain(BasicBlock block) {
218     block.mark();
219     BasicBlock target = block;
220     while (target.isTrivialGoto()) {
221       BasicBlock nextTarget = target.exit().asGoto().getTarget();
222       if (nextTarget.isMarked()) {
223         clearTrivialGotoMarks(block);
224         return nextTarget;
225       }
226       nextTarget.mark();
227       target = nextTarget;
228     }
229     clearTrivialGotoMarks(block);
230     return target;
231   }
232 
clearTrivialGotoMarks(BasicBlock block)233   private static void clearTrivialGotoMarks(BasicBlock block) {
234     while (block.isMarked()) {
235       block.clearMark();
236       if (block.isTrivialGoto()) {
237         block = block.exit().asGoto().getTarget();
238       }
239     }
240   }
241 
collapsTrivialGoto( BasicBlock block, BasicBlock nextBlock, List<BasicBlock> blocksToRemove)242   private static void collapsTrivialGoto(
243       BasicBlock block, BasicBlock nextBlock, List<BasicBlock> blocksToRemove) {
244 
245     // This is the base case for GOTO loops.
246     if (block.exit().asGoto().getTarget() == block) {
247       return;
248     }
249 
250     BasicBlock target = endOfGotoChain(block);
251 
252     boolean needed = false;
253     if (target != nextBlock) {
254       for (BasicBlock pred : block.getPredecessors()) {
255         if (pred.exit().fallthroughBlock() == block) {
256           needed = true;
257           break;
258         }
259       }
260     }
261 
262     // This implies we are in a loop of GOTOs. In that case, we will iteratively remove each trival
263     // GOTO one-by-one until the above base case (one block targeting itself) is left.
264     if (target == block) {
265       target = block.exit().asGoto().getTarget();
266     }
267 
268     if (!needed) {
269       blocksToRemove.add(block);
270       for (BasicBlock pred : block.getPredecessors()) {
271         pred.replaceSuccessor(block, target);
272       }
273       for (BasicBlock succ : block.getSuccessors()) {
274         succ.getPredecessors().remove(block);
275       }
276       for (BasicBlock pred : block.getPredecessors()) {
277         if (!target.getPredecessors().contains(pred)) {
278           target.getPredecessors().add(pred);
279         }
280       }
281     }
282   }
283 
collapsIfTrueTarget(BasicBlock block)284   private static void collapsIfTrueTarget(BasicBlock block) {
285     If insn = block.exit().asIf();
286     BasicBlock target = insn.getTrueTarget();
287     BasicBlock newTarget = endOfGotoChain(target);
288     BasicBlock fallthrough = insn.fallthroughBlock();
289     BasicBlock newFallthrough = endOfGotoChain(fallthrough);
290     if (target != newTarget) {
291       insn.getBlock().replaceSuccessor(target, newTarget);
292       target.getPredecessors().remove(block);
293       if (!newTarget.getPredecessors().contains(block)) {
294         newTarget.getPredecessors().add(block);
295       }
296     }
297     if (block.exit().isIf()) {
298       insn = block.exit().asIf();
299       if (insn.getTrueTarget() == newFallthrough) {
300         // Replace if with the same true and fallthrough target with a goto to the fallthrough.
301         block.replaceSuccessor(insn.getTrueTarget(), fallthrough);
302         assert block.exit().isGoto();
303         assert block.exit().asGoto().getTarget() == fallthrough;
304       }
305     }
306   }
307 
collapsNonFallthroughSwitchTargets(BasicBlock block)308   private static void collapsNonFallthroughSwitchTargets(BasicBlock block) {
309     Switch insn = block.exit().asSwitch();
310     BasicBlock fallthroughBlock = insn.fallthroughBlock();
311     Set<BasicBlock> replacedBlocks = new HashSet<>();
312     for (int j = 0; j < insn.targetBlockIndices().length; j++) {
313       BasicBlock target = insn.targetBlock(j);
314       if (target != fallthroughBlock) {
315         BasicBlock newTarget = endOfGotoChain(target);
316         if (target != newTarget && !replacedBlocks.contains(target)) {
317           insn.getBlock().replaceSuccessor(target, newTarget);
318           target.getPredecessors().remove(block);
319           if (!newTarget.getPredecessors().contains(block)) {
320             newTarget.getPredecessors().add(block);
321           }
322           replacedBlocks.add(target);
323         }
324       }
325     }
326   }
327 
rewriteSwitch(IRCode code)328   public void rewriteSwitch(IRCode code) {
329     for (BasicBlock block : code.blocks) {
330       InstructionListIterator iterator = block.listIterator();
331       while (iterator.hasNext()) {
332         Instruction instruction = iterator.next();
333         if (instruction.isSwitch()) {
334           Switch theSwitch = instruction.asSwitch();
335           if (theSwitch.numberOfKeys() == 1) {
336             // Rewrite the switch to an if.
337             int fallthroughBlockIndex = theSwitch.getFallthroughBlockIndex();
338             int caseBlockIndex = theSwitch.targetBlockIndices()[0];
339             if (fallthroughBlockIndex < caseBlockIndex) {
340               block.swapSuccessorsByIndex(fallthroughBlockIndex, caseBlockIndex);
341             }
342             if (theSwitch.getFirstKey() == 0) {
343               iterator.replaceCurrentInstruction(new If(Type.EQ, theSwitch.value()));
344             } else {
345               ConstNumber labelConst = code.createIntConstant(theSwitch.getFirstKey());
346               iterator.previous();
347               iterator.add(labelConst);
348               Instruction dummy = iterator.next();
349               assert dummy == theSwitch;
350               If theIf = new If(Type.EQ, ImmutableList.of(theSwitch.value(), labelConst.dest()));
351               iterator.replaceCurrentInstruction(theIf);
352             }
353           }
354         }
355       }
356     }
357   }
358 
359   /**
360    * Inline the indirection of switch maps into the switch statement.
361    * <p>
362    * To ensure binary compatibility, javac generated code does not use ordinal values of enums
363    * directly in switch statements but instead generates a companion class that computes a mapping
364    * from switch branches to ordinals at runtime. As we have whole-program knowledge, we can
365    * analyze these maps and inline the indirection into the switch map again.
366    * <p>
367    * In particular, we look for code of the form
368    *
369    * <blockquote><pre>
370    * switch(CompanionClass.$switchmap$field[enumValue.ordinal()]) {
371    *   ...
372    * }
373    * </pre></blockquote>
374    * See {@link #extractIndexMapFrom} and {@link #extractOrdinalsMapFor} for
375    * details of the companion class and ordinals computation.
376    */
removeSwitchMaps(IRCode code)377   public void removeSwitchMaps(IRCode code) {
378     for (BasicBlock block : code.blocks) {
379       InstructionListIterator it = block.listIterator();
380       while (it.hasNext()) {
381         Instruction insn = it.next();
382         // Pattern match a switch on a switch map as input.
383         if (insn.isSwitch()) {
384           Switch switchInsn = insn.asSwitch();
385           Instruction input = switchInsn.inValues().get(0).definition;
386           if (input == null || !input.isArrayGet()) {
387             continue;
388           }
389           ArrayGet arrayGet = input.asArrayGet();
390           Instruction index = arrayGet.index().definition;
391           if (index == null || !index.isInvokeVirtual()) {
392             continue;
393           }
394           InvokeVirtual ordinalInvoke = index.asInvokeVirtual();
395           DexMethod ordinalMethod = ordinalInvoke.getInvokedMethod();
396           DexClass enumClass = appInfo.definitionFor(ordinalMethod.holder);
397           if (enumClass == null
398               || (!enumClass.accessFlags.isEnum() && enumClass.type != dexItemFactory.enumType)
399               || ordinalMethod.name != dexItemFactory.ordinalMethodName
400               || ordinalMethod.proto.returnType != dexItemFactory.intType
401               || !ordinalMethod.proto.parameters.isEmpty()) {
402             continue;
403           }
404           Instruction array = arrayGet.array().definition;
405           if (array == null || !array.isStaticGet()) {
406             continue;
407           }
408           StaticGet staticGet = array.asStaticGet();
409           if (staticGet.getField().name.toSourceString().startsWith("$SwitchMap$")) {
410             Int2ReferenceMap<DexField> indexMap = extractIndexMapFrom(staticGet.getField());
411             if (indexMap == null || indexMap.isEmpty()) {
412               continue;
413             }
414             // Due to member rebinding, only the fields are certain to provide the actual enums
415             // class.
416             DexType switchMapHolder = indexMap.values().iterator().next().getHolder();
417             Reference2IntMap ordinalsMap = extractOrdinalsMapFor(switchMapHolder);
418             if (ordinalsMap != null) {
419               Int2IntMap targetMap = new Int2IntArrayMap();
420               IntList keys = new IntArrayList(switchInsn.numberOfKeys());
421               for (int i = 0; i < switchInsn.numberOfKeys(); i++) {
422                 assert switchInsn.targetBlockIndices()[i] != switchInsn.getFallthroughBlockIndex();
423                 int key = ordinalsMap.getInt(indexMap.get(switchInsn.getKey(i)));
424                 keys.add(key);
425                 targetMap.put(key, switchInsn.targetBlockIndices()[i]);
426               }
427               keys.sort(Comparator.naturalOrder());
428               int[] targets = new int[keys.size()];
429               for (int i = 0; i < keys.size(); i++) {
430                 targets[i] = targetMap.get(keys.getInt(i));
431               }
432 
433               Switch newSwitch = new Switch(ordinalInvoke.outValue(), keys.toIntArray(),
434                   targets, switchInsn.getFallthroughBlockIndex());
435               // Replace the switch itself.
436               it.replaceCurrentInstruction(newSwitch);
437               // If the original input to the switch is now unused, remove it too. It is not dead
438               // as it might have side-effects but we ignore these here.
439               if (arrayGet.outValue().numberOfUsers() == 0) {
440                 arrayGet.inValues().forEach(v -> v.removeUser(arrayGet));
441                 arrayGet.getBlock().removeInstruction(arrayGet);
442               }
443               if (staticGet.outValue().numberOfUsers() == 0) {
444                 assert staticGet.inValues().isEmpty();
445                 staticGet.getBlock().removeInstruction(staticGet);
446               }
447             }
448           }
449         }
450       }
451     }
452   }
453 
454 
455   /**
456    * Extracts the mapping from ordinal values to switch case constants.
457    * <p>
458    * This is done by pattern-matching on the class initializer of the synthetic switch map class.
459    * For a switch
460    *
461    * <blockquote><pre>
462    * switch (day) {
463    *   case WEDNESDAY:
464    *   case FRIDAY:
465    *     System.out.println("3 or 5");
466    *     break;
467    *   case SUNDAY:
468    *     System.out.println("7");
469    *     break;
470    *   default:
471    *     System.out.println("other");
472    * }
473    * </pre></blockquote>
474    *
475    * the generated companing class initializer will have the form
476    *
477    * <blockquote><pre>
478    * class Switches$1 {
479    *   static {
480    *   $SwitchMap$switchmaps$Days[Days.WEDNESDAY.ordinal()] = 1;
481    *   $SwitchMap$switchmaps$Days[Days.FRIDAY.ordinal()] = 2;
482    *   $SwitchMap$switchmaps$Days[Days.SUNDAY.ordinal()] = 3;
483    * }
484    * </pre></blockquote>
485    *
486    * Note that one map per class is generated, so the map might contain additional entries as used
487    * by other switches in the class.
488    */
extractIndexMapFrom(DexField field)489   private Int2ReferenceMap<DexField> extractIndexMapFrom(DexField field) {
490     DexClass clazz = appInfo.definitionFor(field.getHolder());
491     if (!clazz.accessFlags.isSynthetic()) {
492       return null;
493     }
494     DexEncodedMethod initializer = clazz.getClassInitializer();
495     if (initializer == null || initializer.getCode() == null) {
496       return null;
497     }
498     IRCode code = initializer.getCode().buildIR(initializer, new InternalOptions());
499     Int2ReferenceMap<DexField> switchMap = new Int2ReferenceArrayMap<>();
500     for (BasicBlock block : code.blocks) {
501       InstructionListIterator it = block.listIterator();
502       Instruction insn = it.nextUntil(i -> i.isStaticGet() && i.asStaticGet().getField() == field);
503       if (insn == null) {
504         continue;
505       }
506       for (Instruction use : insn.outValue().uniqueUsers()) {
507         if (use.isArrayPut()) {
508           Instruction index = use.asArrayPut().source().definition;
509           if (index == null || !index.isConstNumber()) {
510             return null;
511           }
512           int integerIndex = index.asConstNumber().getIntValue();
513           Instruction value = use.asArrayPut().index().definition;
514           if (value == null || !value.isInvokeVirtual()) {
515             return null;
516           }
517           InvokeVirtual invoke = value.asInvokeVirtual();
518           DexClass holder = appInfo.definitionFor(invoke.getInvokedMethod().holder);
519           if (holder == null ||
520               (!holder.accessFlags.isEnum() && holder.type != dexItemFactory.enumType)) {
521             return null;
522           }
523           Instruction enumGet = invoke.arguments().get(0).definition;
524           if (enumGet == null || !enumGet.isStaticGet()) {
525             return null;
526           }
527           DexField enumField = enumGet.asStaticGet().getField();
528           if (!appInfo.definitionFor(enumField.getHolder()).accessFlags.isEnum()) {
529             return null;
530           }
531           if (switchMap.put(integerIndex, enumField) != null) {
532             return null;
533           }
534         } else {
535           return null;
536         }
537       }
538     }
539     return switchMap;
540   }
541 
542   /**
543    * Extracts the ordinal values for an Enum class from the classes static initializer.
544    * <p>
545    * An Enum class has a field for each value. In the class initializer, each field is initialized
546    * to a singleton object that represents the value. This code matches on the corresponding call
547    * to the constructor (instance initializer) and extracts the value of the second argument, which
548    * is the ordinal.
549    */
extractOrdinalsMapFor(DexType enumClass)550   private Reference2IntMap<DexField> extractOrdinalsMapFor(DexType enumClass) {
551     DexClass clazz = appInfo.definitionFor(enumClass);
552     if (clazz == null || clazz.isLibraryClass()) {
553       // We have to keep binary compatibility in tact for libraries.
554       return null;
555     }
556     DexEncodedMethod initializer = clazz.getClassInitializer();
557     if (!clazz.accessFlags.isEnum() || initializer == null || initializer.getCode() == null) {
558       return null;
559     }
560     IRCode code = initializer.getCode().buildIR(initializer, new InternalOptions());
561     Reference2IntMap<DexField> ordinalsMap = new Reference2IntArrayMap<>();
562     ordinalsMap.defaultReturnValue(-1);
563     InstructionIterator it = code.instructionIterator();
564     while (it.hasNext()) {
565       Instruction insn = it.next();
566       if (!insn.isStaticPut()) {
567         continue;
568       }
569       StaticPut staticPut = insn.asStaticPut();
570       if (staticPut.getField().type != enumClass) {
571         continue;
572       }
573       Instruction newInstance = staticPut.inValue().definition;
574       if (newInstance == null || !newInstance.isNewInstance()) {
575         continue;
576       }
577       Instruction ordinal = null;
578       for (Instruction ctorCall : newInstance.outValue().uniqueUsers()) {
579         if (!ctorCall.isInvokeDirect()) {
580           continue;
581         }
582         InvokeDirect invoke = ctorCall.asInvokeDirect();
583         if (!dexItemFactory.isConstructor(invoke.getInvokedMethod())
584             || invoke.arguments().size() < 3) {
585           continue;
586         }
587         ordinal = invoke.arguments().get(2).definition;
588         break;
589       }
590       if (ordinal == null || !ordinal.isConstNumber()) {
591         return null;
592       }
593       if (ordinalsMap.put(staticPut.getField(), ordinal.asConstNumber().getIntValue()) != -1) {
594         return null;
595       }
596     }
597     return ordinalsMap;
598   }
599 
600   /**
601    * Rewrite all branch targets to the destination of trivial goto chains when possible.
602    * Does not rewrite fallthrough targets as that would require block reordering and the
603    * transformation only makes sense after SSA destruction where there are no phis.
604    */
collapsTrivialGotos(DexEncodedMethod method, IRCode code)605   public static void collapsTrivialGotos(DexEncodedMethod method, IRCode code) {
606     assert code.isConsistentGraph();
607     List<BasicBlock> blocksToRemove = new ArrayList<>();
608     // Rewrite all non-fallthrough targets to the end of trivial goto chains and remove
609     // first round of trivial goto blocks.
610     ListIterator<BasicBlock> iterator = code.listIterator();
611     assert iterator.hasNext();
612     BasicBlock block = iterator.next();
613     BasicBlock nextBlock;
614 
615     // The marks will be used for cycle detection.
616     code.clearMarks();
617     do {
618       nextBlock = iterator.hasNext() ? iterator.next() : null;
619       if (block.isTrivialGoto()) {
620         collapsTrivialGoto(block, nextBlock, blocksToRemove);
621       }
622       if (block.exit().isIf()) {
623         collapsIfTrueTarget(block);
624       }
625       if (block.exit().isSwitch()) {
626         collapsNonFallthroughSwitchTargets(block);
627       }
628       block = nextBlock;
629     } while (nextBlock != null);
630     code.removeBlocks(blocksToRemove);
631     // Get rid of gotos to the next block.
632     while (!blocksToRemove.isEmpty()) {
633       blocksToRemove = new ArrayList<>();
634       iterator = code.listIterator();
635       block = iterator.next();
636       do {
637         nextBlock = iterator.hasNext() ? iterator.next() : null;
638         if (block.isTrivialGoto()) {
639           collapsTrivialGoto(block, nextBlock, blocksToRemove);
640         }
641         block = nextBlock;
642       } while (block != null);
643       code.removeBlocks(blocksToRemove);
644     }
645     assert removedTrivialGotos(code);
646     assert code.isConsistentGraph();
647   }
648 
identifyReturnsArgument( DexEncodedMethod method, IRCode code, OptimizationFeedback feedback)649   public void identifyReturnsArgument(
650       DexEncodedMethod method, IRCode code, OptimizationFeedback feedback) {
651     if (code.getNormalExitBlock() != null) {
652       Return ret = code.getNormalExitBlock().exit().asReturn();
653       if (!ret.isReturnVoid()) {
654         Value returnValue = ret.returnValue();
655         if (returnValue.isArgument()) {
656           // Find the argument number.
657           int index = code.collectArguments().indexOf(returnValue);
658           assert index != -1;
659           feedback.methodReturnsArgument(method, index);
660         }
661         if (returnValue.isConstant() && returnValue.definition.isConstNumber()) {
662           long value = returnValue.definition.asConstNumber().getRawValue();
663           feedback.methodReturnsConstant(method, value);
664         }
665         if (returnValue.isNeverNull()) {
666           feedback.methodNeverReturnsNull(method);
667         }
668       }
669     }
670   }
671 
checkArgumentType(InvokeMethod invoke, DexMethod target, int argumentIndex)672   private boolean checkArgumentType(InvokeMethod invoke, DexMethod target, int argumentIndex) {
673     DexType returnType = invoke.getInvokedMethod().proto.returnType;
674     // TODO(sgjesse): Insert cast if required.
675     if (invoke.isInvokeStatic()) {
676       return invoke.getInvokedMethod().proto.parameters.values[argumentIndex] == returnType;
677     } else {
678       if (argumentIndex == 0) {
679         return invoke.getInvokedMethod().getHolder() == returnType;
680       } else {
681         return invoke.getInvokedMethod().proto.parameters.values[argumentIndex - 1] == returnType;
682       }
683     }
684   }
685 
686   // Replace result uses for methods where something is known about what is returned.
rewriteMoveResult(IRCode code)687   public void rewriteMoveResult(IRCode code) {
688     if (!appInfo.hasSubtyping()) {
689       return;
690     }
691     InstructionIterator iterator = code.instructionIterator();
692     while (iterator.hasNext()) {
693       Instruction current = iterator.next();
694       if (current.isInvokeMethod()) {
695         InvokeMethod invoke = current.asInvokeMethod();
696         if (invoke.outValue() != null) {
697           DexEncodedMethod target = invoke.computeSingleTarget(appInfo.withSubtyping());
698           // We have a set of library classes with optimization information - consider those
699           // as well.
700           if ((target == null) &&
701               libraryClassesWithOptimizationInfo.contains(invoke.getInvokedMethod().getHolder())) {
702             target = appInfo.definitionFor(invoke.getInvokedMethod());
703           }
704           if (target != null) {
705             DexMethod invokedMethod = target.method;
706             // Check if the invoked method is known to return one of its arguments.
707             DexEncodedMethod definition = appInfo.definitionFor(invokedMethod);
708             if (definition != null && definition.getOptimizationInfo().returnsArgument()) {
709               int argumentIndex = definition.getOptimizationInfo().getReturnedArgument();
710               // Replace the out value of the invoke with the argument and ignore the out value.
711               if (argumentIndex != -1 && checkArgumentType(invoke, target.method, argumentIndex)) {
712                 Value argument = invoke.arguments().get(argumentIndex);
713                 assert (invoke.outType() == argument.outType()) ||
714                     (invoke.outType() == MoveType.OBJECT
715                         && argument.outType() == MoveType.SINGLE
716                         && argument.getConstInstruction().asConstNumber().isZero());
717                 invoke.outValue().replaceUsers(argument);
718                 invoke.setOutValue(null);
719               }
720             }
721           }
722         }
723       }
724     }
725     assert code.isConsistentGraph();
726   }
727 
728   // For supporting assert javac adds the static field $assertionsDisabled to all classes which
729   // have methods with assertions. This is used to support the Java VM -ea flag.
730   //
731   //  The class:
732   //
733   //  class A {
734   //    void m() {
735   //      assert xxx;
736   //    }
737   //  }
738   //
739   //  Is compiled into:
740   //
741   //  class A {
742   //    static boolean $assertionsDisabled;
743   //    static {
744   //      $assertionsDisabled = A.class.desiredAssertionStatus();
745   //    }
746   //
747   //    // method with "assert xxx";
748   //    void m() {
749   //      if (!$assertionsDisabled) {
750   //        if (xxx) {
751   //          throw new AssertionError(...);
752   //        }
753   //      }
754   //    }
755   //  }
756   //
757   //  With the rewriting below (and other rewritings) the resulting code is:
758   //
759   //  class A {
760   //    void m() {
761   //    }
762   //  }
763   //
disableAssertions(IRCode code)764   public void disableAssertions(IRCode code) {
765     InstructionIterator iterator = code.instructionIterator();
766     while (iterator.hasNext()) {
767       Instruction current = iterator.next();
768       if (current.isInvokeMethod()) {
769         InvokeMethod invoke = current.asInvokeMethod();
770         if (invoke.getInvokedMethod() == dexItemFactory.classMethods.desiredAssertionStatus) {
771           iterator.replaceCurrentInstruction(code.createFalse());
772         }
773       } else if (current.isStaticPut()) {
774         StaticPut staticPut = current.asStaticPut();
775         if (staticPut.getField().name == dexItemFactory.assertionsDisabled) {
776           iterator.remove();
777         }
778       } else if (current.isStaticGet()) {
779         StaticGet staticGet = current.asStaticGet();
780         if (staticGet.getField().name == dexItemFactory.assertionsDisabled) {
781           iterator.replaceCurrentInstruction(code.createTrue());
782         }
783       }
784     }
785   }
786 
canBeFolded(Instruction instruction)787   private boolean canBeFolded(Instruction instruction) {
788     return (instruction.isBinop() && instruction.asBinop().canBeFolded()) ||
789         (instruction.isUnop() && instruction.asUnop().canBeFolded());
790   }
791 
foldConstants(IRCode code)792   public void foldConstants(IRCode code) {
793     Queue<BasicBlock> worklist = new LinkedList<>();
794     worklist.addAll(code.blocks);
795     for (BasicBlock block = worklist.poll(); block != null; block = worklist.poll()) {
796       InstructionIterator iterator = block.iterator();
797       while (iterator.hasNext()) {
798         Instruction current = iterator.next();
799         Instruction folded;
800         if (canBeFolded(current)) {
801           folded = current.fold(code);
802           iterator.replaceCurrentInstruction(folded);
803           folded.outValue().uniqueUsers()
804               .forEach(instruction -> worklist.add(instruction.getBlock()));
805         }
806       }
807     }
808     assert code.isConsistentSSA();
809   }
810 
811   // Constants are canonicalized in the entry block. We split some of them when it is likely
812   // that having them canonicalized in the entry block will lead to poor code quality.
splitConstants(IRCode code)813   public void splitConstants(IRCode code) {
814     for (BasicBlock block : code.blocks) {
815       // Split constants that flow into phis. It is likely that these constants will have moves
816       // generated for them anyway and we might as well insert a const instruction in the right
817       // predecessor block.
818       splitPhiConstants(code, block);
819       // Split constants that flow into ranged invokes. This gives the register allocator more
820       // freedom in assigning register to ranged invokes which can greatly reduce the number
821       // of register needed (and thereby code size as well).
822       splitRangedInvokeConstants(code, block);
823     }
824   }
825 
splitRangedInvokeConstants(IRCode code, BasicBlock block)826   private void splitRangedInvokeConstants(IRCode code, BasicBlock block) {
827     InstructionListIterator it = block.listIterator();
828     while (it.hasNext()) {
829       Instruction current = it.next();
830       if (current.isInvoke() && current.asInvoke().requiredArgumentRegisters() > 5) {
831         Invoke invoke = current.asInvoke();
832         it.previous();
833         Map<ConstNumber, ConstNumber> oldToNew = new HashMap<>();
834         for (int i = 0; i < invoke.inValues().size(); i++) {
835           Value value = invoke.inValues().get(i);
836           if (value.isConstant() && value.numberOfUsers() > 1) {
837             ConstNumber definition = value.getConstInstruction().asConstNumber();
838             Value originalValue = definition.outValue();
839             ConstNumber newNumber = oldToNew.get(definition);
840             if (newNumber == null) {
841               newNumber = ConstNumber.copyOf(code, definition);
842               it.add(newNumber);
843               oldToNew.put(definition, newNumber);
844             }
845             invoke.inValues().set(i, newNumber.outValue());
846             originalValue.removeUser(invoke);
847             newNumber.outValue().addUser(invoke);
848           }
849         }
850         it.next();
851       }
852     }
853   }
854 
splitPhiConstants(IRCode code, BasicBlock block)855   private void splitPhiConstants(IRCode code, BasicBlock block) {
856     for (int i = 0; i < block.getPredecessors().size(); i++) {
857       Map<ConstNumber, ConstNumber> oldToNew = new IdentityHashMap<>();
858       BasicBlock predecessor = block.getPredecessors().get(i);
859       for (Phi phi : block.getPhis()) {
860         Value operand = phi.getOperand(i);
861         if (!operand.isPhi() && operand.isConstant()) {
862           ConstNumber definition = operand.getConstInstruction().asConstNumber();
863           ConstNumber newNumber = oldToNew.get(definition);
864           Value originalValue = definition.outValue();
865           if (newNumber == null) {
866             newNumber = ConstNumber.copyOf(code, definition);
867             oldToNew.put(definition, newNumber);
868             insertConstantInBlock(newNumber, predecessor);
869           }
870           phi.getOperands().set(i, newNumber.outValue());
871           originalValue.removePhiUser(phi);
872           newNumber.outValue().addPhiUser(phi);
873         }
874       }
875     }
876   }
877 
shortenLiveRanges(IRCode code)878   public void shortenLiveRanges(IRCode code) {
879     // Currently, we are only shortening the live range of constants in the entry block.
880     // TODO(ager): Generalize this to shorten live ranges for more instructions? Currently
881     // doing so seems to make things worse.
882     Map<BasicBlock, List<Instruction>> addConstantInBlock = new HashMap<>();
883     DominatorTree dominatorTree = new DominatorTree(code);
884     BasicBlock block = code.blocks.get(0);
885     InstructionListIterator it = block.listIterator();
886     List<Instruction> toInsertInThisBlock = new ArrayList<>();
887     while (it.hasNext()) {
888       Instruction instruction = it.next();
889       if (instruction.isConstNumber()) {
890         // Collect the blocks for all users of the constant.
891         List<BasicBlock> userBlocks = new LinkedList<>();
892         for (Instruction user : instruction.outValue().uniqueUsers()) {
893           userBlocks.add(user.getBlock());
894         }
895         for (Phi phi : instruction.outValue().uniquePhiUsers()) {
896           userBlocks.add(phi.getBlock());
897         }
898         // Locate the closest dominator block for all user blocks.
899         BasicBlock dominator = dominatorTree.closestDominator(userBlocks);
900         // If the closest dominator block is a block that uses the constant for a phi the constant
901         // needs to go in the immediate dominator block so that it is available for phi moves.
902         for (Phi phi : instruction.outValue().uniquePhiUsers()) {
903           if (phi.getBlock() == dominator) {
904             dominator = dominatorTree.immediateDominator(dominator);
905             break;
906           }
907         }
908         // Move the const instruction as close to its uses as possible.
909         it.detach();
910         if (dominator != block) {
911           // Post-pone constant insertion in order to use a global heuristics.
912           List<Instruction> csts = addConstantInBlock.get(dominator);
913           if (csts == null) {
914             csts = new ArrayList<>();
915             addConstantInBlock.put(dominator, csts);
916           }
917           csts.add(instruction);
918         } else {
919           toInsertInThisBlock.add(instruction);
920         }
921       }
922     }
923 
924     // Heuristic to decide if constant instructions are shared in dominator block of usages or move
925     // to the usages.
926     for (Map.Entry<BasicBlock, List<Instruction>> entry : addConstantInBlock.entrySet()) {
927       if (entry.getValue().size() > STOP_SHARED_CONSTANT_THRESHOLD) {
928         // Too much constants in the same block, do not longer shared them except if they are used
929         // by phi instructions.
930         for (Instruction instruction : entry.getValue()) {
931           if (instruction.outValue().numberOfPhiUsers() != 0) {
932             // Add constant into the dominator block of usages.
933             insertConstantInBlock(instruction, entry.getKey());
934           } else {
935             assert instruction.outValue().numberOfUsers() != 0;
936             ConstNumber constNumber = instruction.asConstNumber();
937             Value constantValue = instruction.outValue();
938             for (Instruction user : constantValue.uniqueUsers()) {
939               ConstNumber newCstNum = ConstNumber.copyOf(code, constNumber);
940               InstructionListIterator iterator = user.getBlock().listIterator(user);
941               iterator.previous();
942               iterator.add(newCstNum);
943               user.replaceValue(constantValue, newCstNum.outValue());
944             }
945           }
946         }
947       } else {
948         // Add constant into the dominator block of usages.
949         for (Instruction inst : entry.getValue()) {
950           insertConstantInBlock(inst, entry.getKey());
951         }
952       }
953     }
954 
955     for (Instruction toInsert : toInsertInThisBlock) {
956       insertConstantInBlock(toInsert, block);
957     }
958     assert code.isConsistentSSA();
959   }
960 
insertConstantInBlock(Instruction instruction, BasicBlock block)961   private void insertConstantInBlock(Instruction instruction, BasicBlock block) {
962     boolean hasCatchHandlers = block.hasCatchHandlers();
963     InstructionListIterator insertAt = block.listIterator();
964     // Place the instruction as late in the block as we can. It needs to go before users
965     // and if we have catch handlers it needs to be placed before the throwing instruction.
966     insertAt.nextUntil(i -> {
967       return i.inValues().contains(instruction.outValue())
968           || i.isJumpInstruction()
969           || (hasCatchHandlers && i.instructionInstanceCanThrow());
970     });
971     insertAt.previous();
972     insertAt.add(instruction);
973   }
974 
computeArrayFilledData( NewArrayEmpty newArray, int size, BasicBlock block, int elementSize)975   private short[] computeArrayFilledData(
976       NewArrayEmpty newArray, int size, BasicBlock block, int elementSize) {
977     ConstNumber[] values = computeConstantArrayValues(newArray, block, size);
978     if (values == null) {
979       return null;
980     }
981     if (elementSize == 1) {
982       short[] result = new short[(size + 1) / 2];
983       for (int i = 0; i < size; i += 2) {
984         short value = (short) (values[i].getIntValue() & 0xFF);
985         if (i + 1 < size) {
986           value |= (short) ((values[i + 1].getIntValue() & 0xFF) << 8);
987         }
988         result[i / 2] = value;
989       }
990       return result;
991     }
992     assert elementSize == 2 || elementSize == 4 || elementSize == 8;
993     int shortsPerConstant = elementSize / 2;
994     short[] result = new short[size * shortsPerConstant];
995     for (int i = 0; i < size; i++) {
996       long value = values[i].getRawValue();
997       for (int part = 0; part < shortsPerConstant; part++) {
998         result[i * shortsPerConstant + part] = (short) ((value >> (16 * part)) & 0xFFFFL);
999       }
1000     }
1001     return result;
1002   }
1003 
computeConstantArrayValues( NewArrayEmpty newArray, BasicBlock block, int size)1004   private ConstNumber[] computeConstantArrayValues(
1005       NewArrayEmpty newArray, BasicBlock block, int size) {
1006     if (size > MAX_FILL_ARRAY_SIZE) {
1007       return null;
1008     }
1009     ConstNumber[] values = new ConstNumber[size];
1010     int remaining = size;
1011     Set<Instruction> users = newArray.outValue().uniqueUsers();
1012     // We allow the array instantiations to cross block boundaries as long as it hasn't encountered
1013     // an instruction instance that can throw an exception.
1014     InstructionListIterator it = block.listIterator();
1015     it.nextUntil(i -> i == newArray);
1016     do {
1017       while (it.hasNext()) {
1018         Instruction instruction = it.next();
1019         // If we encounter an instruction that can throw an exception we need to bail out of the
1020         // optimization so that we do not transform half-initialized arrays into fully initialized
1021         // arrays on exceptional edges.
1022         if (instruction.instructionInstanceCanThrow()) {
1023           return null;
1024         }
1025         if (!users.contains(instruction)) {
1026           continue;
1027         }
1028         // If the initialization sequence is broken by another use we cannot use a
1029         // fill-array-data instruction.
1030         if (!instruction.isArrayPut()) {
1031           return null;
1032         }
1033         ArrayPut arrayPut = instruction.asArrayPut();
1034         if (!arrayPut.source().isConstant()) {
1035           return null;
1036         }
1037         assert arrayPut.index().isConstant();
1038         int index = arrayPut.index().getConstInstruction().asConstNumber().getIntValue();
1039         assert index >= 0 && index < values.length;
1040         if (values[index] != null) {
1041           return null;
1042         }
1043         ConstNumber value = arrayPut.source().getConstInstruction().asConstNumber();
1044         values[index] = value;
1045         --remaining;
1046         if (remaining == 0) {
1047           return values;
1048         }
1049       }
1050       block = block.exit().isGoto() ? block.exit().asGoto().getTarget() : null;
1051       it = block != null ? block.listIterator() : null;
1052     } while (it != null);
1053     return null;
1054   }
1055 
1056   private boolean isPrimitiveNewArrayWithConstantPositiveSize(Instruction instruction) {
1057     if (!(instruction instanceof NewArrayEmpty)) {
1058       return false;
1059     }
1060     NewArrayEmpty newArray = instruction.asNewArrayEmpty();
1061     if (!newArray.size().isConstant()) {
1062       return false;
1063     }
1064     int size = newArray.size().getConstInstruction().asConstNumber().getIntValue();
1065     if (size < 1) {
1066       return false;
1067     }
1068     if (!newArray.type.isPrimitiveArrayType()) {
1069       return false;
1070     }
1071     return true;
1072   }
1073 
1074   /**
1075    * Replace NewArrayEmpty followed by stores of constants to all entries with NewArrayEmpty
1076    * and FillArrayData.
1077    */
1078   public void simplifyArrayConstruction(IRCode code) {
1079     for (BasicBlock block : code.blocks) {
1080       // Map from the array value to the number of array put instruction to remove for that value.
1081       Map<Value, Integer> storesToRemoveForArray = new HashMap<>();
1082       // First pass: identify candidates and insert fill array data instruction.
1083       InstructionListIterator it = block.listIterator();
1084       while (it.hasNext()) {
1085         Instruction instruction = it.next();
1086         if (!isPrimitiveNewArrayWithConstantPositiveSize(instruction)) {
1087           continue;
1088         }
1089         NewArrayEmpty newArray = instruction.asNewArrayEmpty();
1090         int size = newArray.size().getConstInstruction().asConstNumber().getIntValue();
1091         // If there is only one element it is typically smaller to generate the array put
1092         // instruction instead of fill array data.
1093         if (size == 1) {
1094           continue;
1095         }
1096         int elementSize = newArray.type.elementSizeForPrimitiveArrayType();
1097         short[] contents = computeArrayFilledData(newArray, size, block, elementSize);
1098         if (contents == null) {
1099           continue;
1100         }
1101         storesToRemoveForArray.put(newArray.outValue(), size);
1102         int arraySize = newArray.size().getConstInstruction().asConstNumber().getIntValue();
1103         NewArrayFilledData fillArray = new NewArrayFilledData(
1104             newArray.outValue(), elementSize, arraySize, contents);
1105         it.add(fillArray);
1106       }
1107       // Second pass: remove all the array put instructions for the array for which we have
1108       // inserted a fill array data instruction instead.
1109       if (!storesToRemoveForArray.isEmpty()) {
1110         do {
1111           it = block.listIterator();
1112           while (it.hasNext()) {
1113             Instruction instruction = it.next();
1114             if (instruction.isArrayPut()) {
1115               Value array = instruction.asArrayPut().array();
1116               Integer toRemoveCount = storesToRemoveForArray.get(array);
1117               if (toRemoveCount != null && toRemoveCount > 0) {
1118                 storesToRemoveForArray.put(array, toRemoveCount - 1);
1119                 it.remove();
1120               }
1121             }
1122           }
1123           block = block.exit().isGoto() ? block.exit().asGoto().getTarget() : null;
1124         } while (block != null);
1125       }
1126     }
1127   }
1128 
1129   private class ExpressionEquivalence extends Equivalence<Instruction> {
1130 
1131     @Override
1132     protected boolean doEquivalent(Instruction a, Instruction b) {
1133       if (a.getClass() != b.getClass() || !a.identicalNonValueParts(b)) {
1134         return false;
1135       }
1136       // For commutative binary operations any order of in-values are equal.
1137       if (a.isBinop() && a.asBinop().isCommutative()) {
1138         Value a0 = a.inValues().get(0);
1139         Value a1 = a.inValues().get(1);
1140         Value b0 = b.inValues().get(0);
1141         Value b1 = b.inValues().get(1);
1142         return (a0.equals(b0) && a1.equals(b1)) || (a0.equals(b1) && a1.equals(b0));
1143       } else {
1144         // Compare all in-values.
1145         assert a.inValues().size() == b.inValues().size();
1146         for (int i = 0; i < a.inValues().size(); i++) {
1147           if (!a.inValues().get(i).equals(b.inValues().get(i))) {
1148             return false;
1149           }
1150         }
1151         return true;
1152       }
1153     }
1154 
1155     @Override
1156     protected int doHash(Instruction instruction) {
1157       final int prime = 29;
1158       int hash = instruction.getClass().hashCode();
1159       if (instruction.isBinop()) {
1160         Binop binop = instruction.asBinop();
1161         Value in0 = instruction.inValues().get(0);
1162         Value in1 = instruction.inValues().get(1);
1163         if (binop.isCommutative()) {
1164           hash += hash * prime + in0.hashCode() * in1.hashCode();
1165         } else {
1166           hash += hash * prime + in0.hashCode();
1167           hash += hash * prime + in1.hashCode();
1168         }
1169         return hash;
1170       } else {
1171         for (Value value : instruction.inValues()) {
1172           hash += hash * prime + value.hashCode();
1173         }
1174       }
1175       return hash;
1176     }
1177   }
1178 
1179   private boolean shareCatchHandlers(Instruction i0, Instruction i1) {
1180     if (!i0.instructionTypeCanThrow()) {
1181       assert !i1.instructionTypeCanThrow();
1182       return true;
1183     }
1184     assert i1.instructionTypeCanThrow();
1185     // TODO(sgjesse): This could be even better by checking for the exceptions thrown, e.g. div
1186     // and rem only ever throw ArithmeticException.
1187     CatchHandlers<BasicBlock> ch0 = i0.getBlock().getCatchHandlers();
1188     CatchHandlers<BasicBlock> ch1 = i1.getBlock().getCatchHandlers();
1189     return ch0.equals(ch1);
1190   }
1191 
1192   public void commonSubexpressionElimination(IRCode code) {
1193     final ListMultimap<Wrapper<Instruction>, Value> instructionToValue = ArrayListMultimap.create();
1194     final DominatorTree dominatorTree = new DominatorTree(code);
1195     final ExpressionEquivalence equivalence = new ExpressionEquivalence();
1196 
1197     for (int i = 0; i < dominatorTree.getSortedBlocks().length; i++) {
1198       BasicBlock block = dominatorTree.getSortedBlocks()[i];
1199       Iterator<Instruction> iterator = block.iterator();
1200       while (iterator.hasNext()) {
1201         Instruction instruction = iterator.next();
1202         if (instruction.isBinop()
1203             || instruction.isUnop()
1204             || instruction.isInstanceOf()
1205             || instruction.isCheckCast()) {
1206           List<Value> candidates = instructionToValue.get(equivalence.wrap(instruction));
1207           boolean eliminated = false;
1208           if (candidates.size() > 0) {
1209             for (Value candidate : candidates) {
1210               if (dominatorTree.dominatedBy(block, candidate.definition.getBlock()) &&
1211                   shareCatchHandlers(instruction, candidate.definition)) {
1212                 instruction.outValue().replaceUsers(candidate);
1213                 eliminated = true;
1214                 iterator.remove();
1215                 break;  // Don't try any more candidates.
1216               }
1217             }
1218           }
1219           if (!eliminated) {
1220             instructionToValue.put(equivalence.wrap(instruction), instruction.outValue());
1221           }
1222         }
1223       }
1224     }
1225     assert code.isConsistentSSA();
1226   }
1227 
1228   public void simplifyIf(IRCode code) {
1229     DominatorTree dominator = new DominatorTree(code);
1230     code.clearMarks();
1231     for (BasicBlock block : code.blocks) {
1232       if (block.isMarked()) {
1233         continue;
1234       }
1235       if (block.exit().isIf()) {
1236         // First rewrite zero comparison.
1237         rewriteIfWithConstZero(block);
1238 
1239         // Simplify if conditions when possible.
1240         If theIf = block.exit().asIf();
1241         List<Value> inValues = theIf.inValues();
1242         int cond;
1243         if (inValues.get(0).isConstant()
1244             && (theIf.isZeroTest() || inValues.get(1).isConstant())) {
1245           // Zero test with a constant of comparison between between two constants.
1246           if (theIf.isZeroTest()) {
1247             cond = inValues.get(0).getConstInstruction().asConstNumber().getIntValue();
1248           } else {
1249             int left = inValues.get(0).getConstInstruction().asConstNumber().getIntValue();
1250             int right = inValues.get(1).getConstInstruction().asConstNumber().getIntValue();
1251             cond = left - right;
1252           }
1253         } else if (inValues.get(0).hasValueRange()
1254             && (theIf.isZeroTest() || inValues.get(1).hasValueRange())) {
1255           // Zero test with a value range, or comparison between between two values,
1256           // each with a value ranges.
1257           if (theIf.isZeroTest()) {
1258             if (inValues.get(0).isValueInRange(0)) {
1259               // Zero in in the range - can't determine the comparison.
1260               continue;
1261             }
1262             cond = Long.signum(inValues.get(0).getValueRange().getMin());
1263           } else {
1264             LongInterval leftRange = inValues.get(0).getValueRange();
1265             LongInterval rightRange = inValues.get(1).getValueRange();
1266             if (leftRange.overlapsWith(rightRange)) {
1267               // Ranges overlap - can't determine the comparison.
1268               continue;
1269             }
1270             // There is no overlap.
1271             cond = Long.signum(leftRange.getMin() - rightRange.getMin());
1272           }
1273         } else {
1274           continue;
1275         }
1276         BasicBlock target = theIf.targetFromCondition(cond);
1277         BasicBlock deadTarget =
1278             target == theIf.getTrueTarget() ? theIf.fallthroughBlock() : theIf.getTrueTarget();
1279         List<BasicBlock> removedBlocks = block.unlink(deadTarget, dominator);
1280         for (BasicBlock removedBlock : removedBlocks) {
1281           if (!removedBlock.isMarked()) {
1282             removedBlock.mark();
1283           }
1284         }
1285         assert theIf == block.exit();
1286         replaceLastInstruction(block, new Goto());
1287         assert block.exit().isGoto();
1288         assert block.exit().asGoto().getTarget() == target;
1289       }
1290     }
1291     code.removeMarkedBlocks();
1292     assert code.isConsistentSSA();
1293   }
1294 
1295   private void rewriteIfWithConstZero(BasicBlock block) {
1296     If theIf = block.exit().asIf();
1297     if (theIf.isZeroTest()) {
1298       return;
1299     }
1300 
1301     List<Value> inValues = theIf.inValues();
1302     Value leftValue = inValues.get(0);
1303     Value rightValue = inValues.get(1);
1304     if (leftValue.isConstant() || rightValue.isConstant()) {
1305       if (leftValue.isConstant()) {
1306         int left = leftValue.getConstInstruction().asConstNumber().getIntValue();
1307         if (left == 0) {
1308           If ifz = new If(theIf.getType().forSwappedOperands(), rightValue);
1309           replaceLastInstruction(block, ifz);
1310           assert block.exit() == ifz;
1311         }
1312       } else {
1313         assert rightValue.isConstant();
1314         int right = rightValue.getConstInstruction().asConstNumber().getIntValue();
1315         if (right == 0) {
1316           If ifz = new If(theIf.getType(), leftValue);
1317           replaceLastInstruction(block, ifz);
1318           assert block.exit() == ifz;
1319         }
1320       }
1321     }
1322   }
1323 
1324   private void replaceLastInstruction(BasicBlock block, Instruction instruction) {
1325     InstructionListIterator iterator = block.listIterator(block.getInstructions().size());
1326     iterator.previous();
1327     iterator.replaceCurrentInstruction(instruction);
1328   }
1329 
1330   public void rewriteLongCompareAndRequireNonNull(IRCode code, InternalOptions options) {
1331     if (options.canUseLongCompareAndObjectsNonNull()) {
1332       return;
1333     }
1334 
1335     InstructionIterator iterator = code.instructionIterator();
1336     while (iterator.hasNext()) {
1337       Instruction current = iterator.next();
1338       if (current.isInvokeMethod()) {
1339         DexMethod invokedMethod = current.asInvokeMethod().getInvokedMethod();
1340         if (invokedMethod == dexItemFactory.longMethods.compare) {
1341           // Rewrite calls to Long.compare for sdk versions that do not have that method.
1342           List<Value> inValues = current.inValues();
1343           assert inValues.size() == 2;
1344           iterator.replaceCurrentInstruction(
1345               new Cmp(NumericType.LONG, Bias.NONE, current.outValue(), inValues.get(0),
1346                   inValues.get(1)));
1347         } else if (invokedMethod == dexItemFactory.objectsMethods.requireNonNull) {
1348           // Rewrite calls to Objects.requireNonNull(Object) because Javac 9 start to use it for
1349           // synthesized null checks.
1350           InvokeVirtual callToGetClass = new InvokeVirtual(dexItemFactory.objectMethods.getClass,
1351               null, current.inValues());
1352           if (current.outValue() != null) {
1353             current.outValue().replaceUsers(current.inValues().get(0));
1354             current.setOutValue(null);
1355           }
1356           iterator.replaceCurrentInstruction(callToGetClass);
1357         }
1358       }
1359     }
1360     assert code.isConsistentSSA();
1361   }
1362 
1363   // Removes calls to Throwable.addSuppressed(Throwable) and rewrites
1364   // Throwable.getSuppressed() into new Throwable[0].
1365   //
1366   // Note that addSuppressed() and getSuppressed() methods are final in
1367   // Throwable, so these changes don't have to worry about overrides.
1368   public void rewriteThrowableAddAndGetSuppressed(IRCode code) {
1369     DexItemFactory.ThrowableMethods throwableMethods = dexItemFactory.throwableMethods;
1370 
1371     for (BasicBlock block : code.blocks) {
1372       InstructionListIterator iterator = block.listIterator();
1373       while (iterator.hasNext()) {
1374         Instruction current = iterator.next();
1375         if (current.isInvokeMethod()) {
1376           DexMethod invokedMethod = current.asInvokeMethod().getInvokedMethod();
1377 
1378           if (matchesMethodOfThrowable(invokedMethod, throwableMethods.addSuppressed)) {
1379             // Remove Throwable::addSuppressed(Throwable) call.
1380             iterator.remove();
1381           } else if (matchesMethodOfThrowable(invokedMethod, throwableMethods.getSuppressed)) {
1382             Value destValue = current.outValue();
1383             if (destValue == null) {
1384               // If the result of the call was not used we don't create
1385               // an empty array and just remove the call.
1386               iterator.remove();
1387               continue;
1388             }
1389 
1390             // Replace call to Throwable::getSuppressed() with new Throwable[0].
1391 
1392             // First insert the constant value *before* the current instruction.
1393             ConstNumber zero = code.createIntConstant(0);
1394             assert iterator.hasPrevious();
1395             iterator.previous();
1396             iterator.add(zero);
1397 
1398             // Then replace the invoke instruction with NewArrayEmpty instruction.
1399             Instruction next = iterator.next();
1400             assert current == next;
1401             NewArrayEmpty newArray = new NewArrayEmpty(destValue, zero.outValue(),
1402                 dexItemFactory.createType(dexItemFactory.throwableArrayDescriptor));
1403             iterator.replaceCurrentInstruction(newArray);
1404           }
1405         }
1406       }
1407     }
1408     assert code.isConsistentSSA();
1409   }
1410 
1411   private boolean matchesMethodOfThrowable(DexMethod invoked, DexMethod expected) {
1412     return invoked.name == expected.name
1413         && invoked.proto == expected.proto
1414         && isSubtypeOfThrowable(invoked.holder);
1415   }
1416 
1417   private boolean isSubtypeOfThrowable(DexType type) {
1418     while (type != null && type != dexItemFactory.objectType) {
1419       if (type == dexItemFactory.throwableType) {
1420         return true;
1421       }
1422       DexClass dexClass = appInfo.definitionFor(type);
1423       if (dexClass == null) {
1424         throw new CompilationError("Class or interface " + type.toSourceString() +
1425             " required for desugaring of try-with-resources is not found.");
1426       }
1427       type = dexClass.superType;
1428     }
1429     return false;
1430   }
1431 
1432   private Value addConstString(IRCode code, InstructionListIterator iterator, String s) {
1433     Value value = code.createValue(MoveType.OBJECT);;
1434     iterator.add(new ConstString(value, dexItemFactory.createString(s)));
1435     return value;
1436   }
1437 
1438   /**
1439    * Insert code into <code>method</code> to log the argument types to System.out.
1440    *
1441    * The type is determined by calling getClass() on the argument.
1442    */
1443   public void logArgumentTypes(DexEncodedMethod method, IRCode code) {
1444     List<Value> arguments = code.collectArguments();
1445     BasicBlock block = code.blocks.getFirst();
1446     InstructionListIterator iterator = block.listIterator();
1447 
1448     // Split arguments into their own block.
1449     iterator.nextUntil(instruction -> !instruction.isArgument());
1450     iterator.previous();
1451     iterator.split(code);
1452     iterator.previous();
1453 
1454     // Now that the block is split there should not be any catch handlers in the block.
1455     assert !block.hasCatchHandlers();
1456     Value out = code.createValue(MoveType.OBJECT);
1457     DexType javaLangSystemType = dexItemFactory.createType("Ljava/lang/System;");
1458     DexType javaIoPrintStreamType = dexItemFactory.createType("Ljava/io/PrintStream;");
1459 
1460     DexProto proto = dexItemFactory.createProto(
1461         dexItemFactory.voidType, new DexType[]{dexItemFactory.objectType});
1462     DexMethod print = dexItemFactory.createMethod(javaIoPrintStreamType, proto, "print");
1463     DexMethod printLn = dexItemFactory.createMethod(javaIoPrintStreamType, proto, "println");
1464 
1465     iterator.add(
1466         new StaticGet(MemberType.OBJECT, out,
1467             dexItemFactory.createField(javaLangSystemType, javaIoPrintStreamType, "out")));
1468 
1469     Value value = code.createValue(MoveType.OBJECT);
1470     iterator.add(new ConstString(value, dexItemFactory.createString("INVOKE ")));
1471     iterator.add(new InvokeVirtual(print, null, ImmutableList.of(out, value)));
1472 
1473     value = code.createValue(MoveType.OBJECT);;
1474     iterator.add(
1475         new ConstString(value, dexItemFactory.createString(method.method.qualifiedName())));
1476     iterator.add(new InvokeVirtual(print, null, ImmutableList.of(out, value)));
1477 
1478     Value openParenthesis = addConstString(code, iterator, "(");
1479     Value comma = addConstString(code, iterator, ",");
1480     Value closeParenthesis = addConstString(code, iterator, ")");
1481     Value indent = addConstString(code, iterator, "  ");
1482     Value nul = addConstString(code, iterator, "(null)");
1483     Value primitive = addConstString(code, iterator, "(primitive)");
1484     Value empty = addConstString(code, iterator, "");
1485 
1486     iterator.add(new InvokeVirtual(printLn, null, ImmutableList.of(out, openParenthesis)));
1487     for (int i = 0; i < arguments.size(); i++) {
1488       iterator.add(new InvokeVirtual(print, null, ImmutableList.of(out, indent)));
1489 
1490       // Add a block for end-of-line printing.
1491       BasicBlock eol = BasicBlock.createGotoBlock(code.blocks.size());
1492       code.blocks.add(eol);
1493 
1494       BasicBlock successor = block.unlinkSingleSuccessor();
1495       block.link(eol);
1496       eol.link(successor);
1497 
1498       Value argument = arguments.get(i);
1499       if (argument.outType() != MoveType.OBJECT) {
1500         iterator.add(new InvokeVirtual(print, null, ImmutableList.of(out, primitive)));
1501       } else {
1502         // Insert "if (argument != null) ...".
1503         successor = block.unlinkSingleSuccessor();
1504         If theIf = new If(If.Type.NE, argument);
1505         BasicBlock ifBlock = BasicBlock.createIfBlock(code.blocks.size(), theIf);
1506         code.blocks.add(ifBlock);
1507         // Fallthrough block must be added right after the if.
1508         BasicBlock isNullBlock = BasicBlock.createGotoBlock(code.blocks.size());
1509         code.blocks.add(isNullBlock);
1510         BasicBlock isNotNullBlock = BasicBlock.createGotoBlock(code.blocks.size());
1511         code.blocks.add(isNotNullBlock);
1512 
1513         // Link the added blocks together.
1514         block.link(ifBlock);
1515         ifBlock.link(isNotNullBlock);
1516         ifBlock.link(isNullBlock);
1517         isNotNullBlock.link(successor);
1518         isNullBlock.link(successor);
1519 
1520         // Fill code into the blocks.
1521         iterator = isNullBlock.listIterator();
1522         iterator.add(new InvokeVirtual(print, null, ImmutableList.of(out, nul)));
1523         iterator = isNotNullBlock.listIterator();
1524         value = code.createValue(MoveType.OBJECT);
1525         iterator.add(new InvokeVirtual(dexItemFactory.objectMethods.getClass, value,
1526             ImmutableList.of(arguments.get(i))));
1527         iterator.add(new InvokeVirtual(print, null, ImmutableList.of(out, value)));
1528       }
1529 
1530       iterator = eol.listIterator();
1531       if (i == arguments.size() - 1) {
1532         iterator.add(new InvokeVirtual(printLn, null, ImmutableList.of(out, closeParenthesis)));
1533       } else {
1534         iterator.add(new InvokeVirtual(printLn, null, ImmutableList.of(out, comma)));
1535       }
1536       block = eol;
1537     }
1538     // When we fall out of the loop the iterator is in the last eol block.
1539     iterator.add(new InvokeVirtual(printLn, null, ImmutableList.of(out, empty)));
1540   }
1541 }
1542