• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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 package com.android.dx.ssa;
18 
19 import com.android.dx.rop.code.Exceptions;
20 import com.android.dx.rop.code.FillArrayDataInsn;
21 import com.android.dx.rop.code.Insn;
22 import com.android.dx.rop.code.PlainCstInsn;
23 import com.android.dx.rop.code.PlainInsn;
24 import com.android.dx.rop.code.RegOps;
25 import com.android.dx.rop.code.RegisterSpec;
26 import com.android.dx.rop.code.RegisterSpecList;
27 import com.android.dx.rop.code.Rop;
28 import com.android.dx.rop.code.Rops;
29 import com.android.dx.rop.code.ThrowingCstInsn;
30 import com.android.dx.rop.code.ThrowingInsn;
31 import com.android.dx.rop.cst.Constant;
32 import com.android.dx.rop.cst.CstLiteralBits;
33 import com.android.dx.rop.cst.CstMethodRef;
34 import com.android.dx.rop.cst.CstNat;
35 import com.android.dx.rop.cst.CstString;
36 import com.android.dx.rop.cst.CstType;
37 import com.android.dx.rop.cst.TypedConstant;
38 import com.android.dx.rop.cst.Zeroes;
39 import com.android.dx.rop.type.StdTypeList;
40 import com.android.dx.rop.type.Type;
41 import com.android.dx.rop.type.TypeBearer;
42 
43 import java.util.ArrayList;
44 import java.util.BitSet;
45 import java.util.HashSet;
46 import java.util.List;
47 
48 /**
49  * Simple intraprocedural escape analysis. Finds new arrays that don't escape
50  * the method they are created in and replaces the array values with registers.
51  */
52 public class EscapeAnalysis {
53     /**
54      * Struct used to generate and maintain escape analysis results.
55      */
56     static class EscapeSet {
57         /** set containing all registers related to an object */
58         BitSet regSet;
59         /** escape state of the object */
60         EscapeState escape;
61         /** list of objects that are put into this object */
62         ArrayList<EscapeSet> childSets;
63         /** list of objects that this object is put into */
64         ArrayList<EscapeSet> parentSets;
65         /** flag to indicate this object is a scalar replaceable array */
66         boolean replaceableArray;
67 
68         /**
69          * Constructs an instance of an EscapeSet
70          *
71          * @param reg the SSA register that defines the object
72          * @param size the number of registers in the method
73          * @param escState the lattice value to initially set this to
74          */
EscapeSet(int reg, int size, EscapeState escState)75         EscapeSet(int reg, int size, EscapeState escState) {
76             regSet = new BitSet(size);
77             regSet.set(reg);
78             escape = escState;
79             childSets = new ArrayList<EscapeSet>();
80             parentSets = new ArrayList<EscapeSet>();
81             replaceableArray = false;
82         }
83     }
84 
85     /**
86      * Lattice values used to indicate escape state for an object. Analysis can
87      * only raise escape state values, not lower them.
88      *
89      * TOP - Used for objects that haven't been analyzed yet
90      * NONE - Object does not escape, and is eligible for scalar replacement.
91      * METHOD - Object remains local to method, but can't be scalar replaced.
92      * INTER - Object is passed between methods. (treated as globally escaping
93      *         since this is an intraprocedural analysis)
94      * GLOBAL - Object escapes globally.
95      */
96     public enum EscapeState {
97         TOP, NONE, METHOD, INTER, GLOBAL
98     }
99 
100     /** method we're processing */
101     private SsaMethod ssaMeth;
102     /** ssaMeth.getRegCount() */
103     private int regCount;
104     /** Lattice values for each object register group */
105     private ArrayList<EscapeSet> latticeValues;
106 
107     /**
108      * Constructs an instance.
109      *
110      * @param ssaMeth method to process
111      */
EscapeAnalysis(SsaMethod ssaMeth)112     private EscapeAnalysis(SsaMethod ssaMeth) {
113         this.ssaMeth = ssaMeth;
114         this.regCount = ssaMeth.getRegCount();
115         this.latticeValues = new ArrayList<EscapeSet>();
116     }
117 
118     /**
119      * Finds the index in the lattice for a particular register.
120      * Returns the size of the lattice if the register wasn't found.
121      *
122      * @param reg {@code non-null;} register being looked up
123      * @return index of the register or size of the lattice if it wasn't found.
124      */
findSetIndex(RegisterSpec reg)125     private int findSetIndex(RegisterSpec reg) {
126         int i;
127         for (i = 0; i < latticeValues.size(); i++) {
128             EscapeSet e = latticeValues.get(i);
129             if (e.regSet.get(reg.getReg())) {
130                 return i;
131             }
132         }
133         return i;
134     }
135 
136     /**
137      * Finds the corresponding instruction for a given move result
138      *
139      * @param moveInsn {@code non-null;} a move result instruction
140      * @return {@code non-null;} the instruction that produces the result for
141      * the move
142      */
getInsnForMove(SsaInsn moveInsn)143     private SsaInsn getInsnForMove(SsaInsn moveInsn) {
144         int pred = moveInsn.getBlock().getPredecessors().nextSetBit(0);
145         ArrayList<SsaInsn> predInsns = ssaMeth.getBlocks().get(pred).getInsns();
146         return predInsns.get(predInsns.size()-1);
147     }
148 
149     /**
150      * Finds the corresponding move result for a given instruction
151      *
152      * @param insn {@code non-null;} an instruction that must always be
153      * followed by a move result
154      * @return {@code non-null;} the move result for the given instruction
155      */
getMoveForInsn(SsaInsn insn)156     private SsaInsn getMoveForInsn(SsaInsn insn) {
157         int succ = insn.getBlock().getSuccessors().nextSetBit(0);
158         ArrayList<SsaInsn> succInsns = ssaMeth.getBlocks().get(succ).getInsns();
159         return succInsns.get(0);
160     }
161 
162     /**
163      * Creates a link in the lattice between two EscapeSets due to a put
164      * instruction. The object being put is the child and the object being put
165      * into is the parent. A child set must always have an escape state at
166      * least as high as its parent.
167      *
168      * @param parentSet {@code non-null;} the EscapeSet for the object being put
169      * into
170      * @param childSet {@code non-null;} the EscapeSet for the object being put
171      */
addEdge(EscapeSet parentSet, EscapeSet childSet)172     private void addEdge(EscapeSet parentSet, EscapeSet childSet) {
173         if (!childSet.parentSets.contains(parentSet)) {
174             childSet.parentSets.add(parentSet);
175         }
176         if (!parentSet.childSets.contains(childSet)) {
177             parentSet.childSets.add(childSet);
178         }
179     }
180 
181     /**
182      * Merges all links in the lattice among two EscapeSets. On return, the
183      * newNode will have its old links as well as all links from the oldNode.
184      * The oldNode has all its links removed.
185      *
186      * @param newNode {@code non-null;} the EscapeSet to merge all links into
187      * @param oldNode {@code non-null;} the EscapeSet to remove all links from
188      */
replaceNode(EscapeSet newNode, EscapeSet oldNode)189     private void replaceNode(EscapeSet newNode, EscapeSet oldNode) {
190         for (EscapeSet e : oldNode.parentSets) {
191             e.childSets.remove(oldNode);
192             e.childSets.add(newNode);
193             newNode.parentSets.add(e);
194         }
195         for (EscapeSet e : oldNode.childSets) {
196             e.parentSets.remove(oldNode);
197             e.parentSets.add(newNode);
198             newNode.childSets.add(e);
199         }
200     }
201 
202     /**
203      * Performs escape analysis on a method. Finds scalar replaceable arrays and
204      * replaces them with equivalent registers.
205      *
206      * @param ssaMethod {@code non-null;} method to process
207      */
process(SsaMethod ssaMethod)208     public static void process(SsaMethod ssaMethod) {
209         new EscapeAnalysis(ssaMethod).run();
210     }
211 
212     /**
213      * Process a single instruction, looking for new objects resulting from
214      * move result or move param.
215      *
216      * @param insn {@code non-null;} instruction to process
217      */
processInsn(SsaInsn insn)218     private void processInsn(SsaInsn insn) {
219         int op = insn.getOpcode().getOpcode();
220         RegisterSpec result = insn.getResult();
221         EscapeSet escSet;
222 
223         // Identify new objects
224         if (op == RegOps.MOVE_RESULT_PSEUDO &&
225                 result.getTypeBearer().getBasicType() == Type.BT_OBJECT) {
226             // Handle objects generated through move_result_pseudo
227             escSet = processMoveResultPseudoInsn(insn);
228             processRegister(result, escSet);
229         } else if (op == RegOps.MOVE_PARAM &&
230                       result.getTypeBearer().getBasicType() == Type.BT_OBJECT) {
231             // Track method arguments that are objects
232             escSet = new EscapeSet(result.getReg(), regCount, EscapeState.NONE);
233             latticeValues.add(escSet);
234             processRegister(result, escSet);
235         } else if (op == RegOps.MOVE_RESULT &&
236                 result.getTypeBearer().getBasicType() == Type.BT_OBJECT) {
237             // Track method return values that are objects
238             escSet = new EscapeSet(result.getReg(), regCount, EscapeState.NONE);
239             latticeValues.add(escSet);
240             processRegister(result, escSet);
241         }
242     }
243 
244     /**
245      * Determine the origin of a move result pseudo instruction that generates
246      * an object. Creates a new EscapeSet for the new object accordingly.
247      *
248      * @param insn {@code non-null;} move result pseudo instruction to process
249      * @return {@code non-null;} an EscapeSet for the object referred to by the
250      * move result pseudo instruction
251      */
processMoveResultPseudoInsn(SsaInsn insn)252     private EscapeSet processMoveResultPseudoInsn(SsaInsn insn) {
253         RegisterSpec result = insn.getResult();
254         SsaInsn prevSsaInsn = getInsnForMove(insn);
255         int prevOpcode = prevSsaInsn.getOpcode().getOpcode();
256         EscapeSet escSet;
257         RegisterSpec prevSource;
258 
259         switch(prevOpcode) {
260            // New instance / Constant
261             case RegOps.NEW_INSTANCE:
262             case RegOps.CONST:
263                 escSet = new EscapeSet(result.getReg(), regCount,
264                                            EscapeState.NONE);
265                 break;
266             // New array
267             case RegOps.NEW_ARRAY:
268             case RegOps.FILLED_NEW_ARRAY:
269                 prevSource = prevSsaInsn.getSources().get(0);
270                 if (prevSource.getTypeBearer().isConstant()) {
271                     // New fixed array
272                     escSet = new EscapeSet(result.getReg(), regCount,
273                                                EscapeState.NONE);
274                     escSet.replaceableArray = true;
275                 } else {
276                     // New variable array
277                     escSet = new EscapeSet(result.getReg(), regCount,
278                                                EscapeState.GLOBAL);
279                 }
280                 break;
281             // Loading a static object
282             case RegOps.GET_STATIC:
283                 escSet = new EscapeSet(result.getReg(), regCount,
284                                            EscapeState.GLOBAL);
285                 break;
286             // Type cast / load an object from a field or array
287             case RegOps.CHECK_CAST:
288             case RegOps.GET_FIELD:
289             case RegOps.AGET:
290                 prevSource = prevSsaInsn.getSources().get(0);
291                 int setIndex = findSetIndex(prevSource);
292 
293                 // Set should already exist, try to find it
294                 if (setIndex != latticeValues.size()) {
295                     escSet = latticeValues.get(setIndex);
296                     escSet.regSet.set(result.getReg());
297                     return escSet;
298                 }
299 
300                 // Set not found, must be either null or unknown
301                 if (prevSource.getType() == Type.KNOWN_NULL) {
302                     escSet = new EscapeSet(result.getReg(), regCount,
303                                                EscapeState.NONE);
304                } else {
305                     escSet = new EscapeSet(result.getReg(), regCount,
306                                                EscapeState.GLOBAL);
307                 }
308                 break;
309             default:
310                 return null;
311         }
312 
313         // Add the newly created escSet to the lattice and return it
314         latticeValues.add(escSet);
315         return escSet;
316     }
317 
318     /**
319      * Iterate through all the uses of a new object.
320      *
321      * @param result {@code non-null;} register where new object is stored
322      * @param escSet {@code non-null;} EscapeSet for the new object
323      */
processRegister(RegisterSpec result, EscapeSet escSet)324     private void processRegister(RegisterSpec result, EscapeSet escSet) {
325         ArrayList<RegisterSpec> regWorklist = new ArrayList<RegisterSpec>();
326         regWorklist.add(result);
327 
328         // Go through the worklist
329         while (!regWorklist.isEmpty()) {
330             int listSize = regWorklist.size() - 1;
331             RegisterSpec def = regWorklist.remove(listSize);
332             List<SsaInsn> useList = ssaMeth.getUseListForRegister(def.getReg());
333 
334             // Handle all the uses of this register
335             for (SsaInsn use : useList) {
336                 Rop useOpcode = use.getOpcode();
337 
338                 if (useOpcode == null) {
339                     // Handle phis
340                     processPhiUse(use, escSet, regWorklist);
341                 } else {
342                     // Handle other opcodes
343                     processUse(def, use, escSet, regWorklist);
344                 }
345             }
346         }
347     }
348 
349     /**
350      * Handles phi uses of new objects. Will merge together the sources of a phi
351      * into a single EscapeSet. Adds the result of the phi to the worklist so
352      * its uses can be followed.
353      *
354      * @param use {@code non-null;} phi use being processed
355      * @param escSet {@code non-null;} EscapeSet for the object
356      * @param regWorklist {@code non-null;} worklist of instructions left to
357      * process for this object
358      */
processPhiUse(SsaInsn use, EscapeSet escSet, ArrayList<RegisterSpec> regWorklist)359     private void processPhiUse(SsaInsn use, EscapeSet escSet,
360                                    ArrayList<RegisterSpec> regWorklist) {
361         int setIndex = findSetIndex(use.getResult());
362         if (setIndex != latticeValues.size()) {
363             // Check if result is in a set already
364             EscapeSet mergeSet = latticeValues.get(setIndex);
365             if (mergeSet != escSet) {
366                 // If it is, merge the sets and states, then delete the copy
367                 escSet.replaceableArray = false;
368                 escSet.regSet.or(mergeSet.regSet);
369                 if (escSet.escape.compareTo(mergeSet.escape) < 0) {
370                     escSet.escape = mergeSet.escape;
371                 }
372                 replaceNode(escSet, mergeSet);
373                 latticeValues.remove(setIndex);
374             }
375         } else {
376             // If no set is found, add it to this escSet and the worklist
377             escSet.regSet.set(use.getResult().getReg());
378             regWorklist.add(use.getResult());
379         }
380     }
381 
382     /**
383      * Handles non-phi uses of new objects. Checks to see how instruction is
384      * used and updates the escape state accordingly.
385      *
386      * @param def {@code non-null;} register holding definition of new object
387      * @param use {@code non-null;} use of object being processed
388      * @param escSet {@code non-null;} EscapeSet for the object
389      * @param regWorklist {@code non-null;} worklist of instructions left to
390      * process for this object
391      */
processUse(RegisterSpec def, SsaInsn use, EscapeSet escSet, ArrayList<RegisterSpec> regWorklist)392     private void processUse(RegisterSpec def, SsaInsn use, EscapeSet escSet,
393                                 ArrayList<RegisterSpec> regWorklist) {
394         int useOpcode = use.getOpcode().getOpcode();
395         switch (useOpcode) {
396             case RegOps.MOVE:
397                 // Follow uses of the move by adding it to the worklist
398                 escSet.regSet.set(use.getResult().getReg());
399                 regWorklist.add(use.getResult());
400                 break;
401             case RegOps.IF_EQ:
402             case RegOps.IF_NE:
403             case RegOps.CHECK_CAST:
404                 // Compared objects can't be replaced, so promote if necessary
405                 if (escSet.escape.compareTo(EscapeState.METHOD) < 0) {
406                     escSet.escape = EscapeState.METHOD;
407                 }
408                 break;
409             case RegOps.APUT:
410                 // For array puts, check for a constant array index
411                 RegisterSpec putIndex = use.getSources().get(2);
412                 if (!putIndex.getTypeBearer().isConstant()) {
413                     // If not constant, array can't be replaced
414                     escSet.replaceableArray = false;
415                 }
416                 // Intentional fallthrough
417             case RegOps.PUT_FIELD:
418                 // Skip non-object puts
419                 RegisterSpec putValue = use.getSources().get(0);
420                 if (putValue.getTypeBearer().getBasicType() != Type.BT_OBJECT) {
421                     break;
422                 }
423                 escSet.replaceableArray = false;
424 
425                 // Raise 1st object's escape state to 2nd if 2nd is higher
426                 RegisterSpecList sources = use.getSources();
427                 if (sources.get(0).getReg() == def.getReg()) {
428                     int setIndex = findSetIndex(sources.get(1));
429                     if (setIndex != latticeValues.size()) {
430                         EscapeSet parentSet = latticeValues.get(setIndex);
431                         addEdge(parentSet, escSet);
432                         if (escSet.escape.compareTo(parentSet.escape) < 0) {
433                             escSet.escape = parentSet.escape;
434                         }
435                     }
436                 } else {
437                     int setIndex = findSetIndex(sources.get(0));
438                     if (setIndex != latticeValues.size()) {
439                         EscapeSet childSet = latticeValues.get(setIndex);
440                         addEdge(escSet, childSet);
441                         if (childSet.escape.compareTo(escSet.escape) < 0) {
442                             childSet.escape = escSet.escape;
443                         }
444                     }
445                 }
446                 break;
447             case RegOps.AGET:
448                 // For array gets, check for a constant array index
449                 RegisterSpec getIndex = use.getSources().get(1);
450                 if (!getIndex.getTypeBearer().isConstant()) {
451                     // If not constant, array can't be replaced
452                     escSet.replaceableArray = false;
453                 }
454                 break;
455             case RegOps.PUT_STATIC:
456                 // Static puts cause an object to escape globally
457                 escSet.escape = EscapeState.GLOBAL;
458                 break;
459             case RegOps.INVOKE_STATIC:
460             case RegOps.INVOKE_VIRTUAL:
461             case RegOps.INVOKE_SUPER:
462             case RegOps.INVOKE_DIRECT:
463             case RegOps.INVOKE_INTERFACE:
464             case RegOps.RETURN:
465             case RegOps.THROW:
466                 // These operations cause an object to escape interprocedurally
467                 escSet.escape = EscapeState.INTER;
468                 break;
469             default:
470                 break;
471         }
472     }
473 
474     /**
475      * Performs scalar replacement on all eligible arrays.
476      */
scalarReplacement()477     private void scalarReplacement() {
478         // Iterate through lattice, looking for non-escaping replaceable arrays
479         for (EscapeSet escSet : latticeValues) {
480             if (!escSet.replaceableArray || escSet.escape != EscapeState.NONE) {
481                 continue;
482             }
483 
484             // Get the instructions for the definition and move of the array
485             int e = escSet.regSet.nextSetBit(0);
486             SsaInsn def = ssaMeth.getDefinitionForRegister(e);
487             SsaInsn prev = getInsnForMove(def);
488 
489             // Create a map for the new registers that will be created
490             TypeBearer lengthReg = prev.getSources().get(0).getTypeBearer();
491             int length = ((CstLiteralBits) lengthReg).getIntBits();
492             ArrayList<RegisterSpec> newRegs =
493                 new ArrayList<RegisterSpec>(length);
494             HashSet<SsaInsn> deletedInsns = new HashSet<SsaInsn>();
495 
496             // Replace the definition of the array with registers
497             replaceDef(def, prev, length, newRegs);
498 
499             // Mark definition instructions for deletion
500             deletedInsns.add(prev);
501             deletedInsns.add(def);
502 
503             // Go through all uses of the array
504             List<SsaInsn> useList = ssaMeth.getUseListForRegister(e);
505             for (SsaInsn use : useList) {
506                 // Replace the use with scalars and then mark it for deletion
507                 replaceUse(use, prev, newRegs, deletedInsns);
508                 deletedInsns.add(use);
509             }
510 
511             // Delete all marked instructions
512             ssaMeth.deleteInsns(deletedInsns);
513             ssaMeth.onInsnsChanged();
514 
515             // Convert the method back to SSA form
516             SsaConverter.updateSsaMethod(ssaMeth, regCount);
517 
518             // Propagate and remove extra moves added by scalar replacement
519             movePropagate();
520         }
521     }
522 
523     /**
524      * Replaces the instructions that define an array with equivalent registers.
525      * For each entry in the array, a register is created, initialized to zero.
526      * A mapping between this register and the corresponding array index is
527      * added.
528      *
529      * @param def {@code non-null;} move result instruction for array
530      * @param prev {@code non-null;} instruction for instantiating new array
531      * @param length size of the new array
532      * @param newRegs {@code non-null;} mapping of array indices to new
533      * registers to be populated
534      */
replaceDef(SsaInsn def, SsaInsn prev, int length, ArrayList<RegisterSpec> newRegs)535     private void replaceDef(SsaInsn def, SsaInsn prev, int length,
536                                 ArrayList<RegisterSpec> newRegs) {
537         Type resultType = def.getResult().getType();
538 
539         // Create new zeroed out registers for each element in the array
540         for (int i = 0; i < length; i++) {
541             Constant newZero = Zeroes.zeroFor(resultType.getComponentType());
542             TypedConstant typedZero = (TypedConstant) newZero;
543             RegisterSpec newReg =
544                 RegisterSpec.make(ssaMeth.makeNewSsaReg(), typedZero);
545             newRegs.add(newReg);
546             insertPlainInsnBefore(def, RegisterSpecList.EMPTY, newReg,
547                                       RegOps.CONST, newZero);
548         }
549     }
550 
551     /**
552      * Replaces the use for a scalar replaceable array. Gets and puts become
553      * move instructions, and array lengths and fills are handled. Can also
554      * identify ArrayIndexOutOfBounds exceptions and throw them if detected.
555      *
556      * @param use {@code non-null;} move result instruction for array
557      * @param prev {@code non-null;} instruction for instantiating new array
558      * @param newRegs {@code non-null;} mapping of array indices to new
559      * registers
560      * @param deletedInsns {@code non-null;} set of instructions marked for
561      * deletion
562      */
replaceUse(SsaInsn use, SsaInsn prev, ArrayList<RegisterSpec> newRegs, HashSet<SsaInsn> deletedInsns)563     private void replaceUse(SsaInsn use, SsaInsn prev,
564                                 ArrayList<RegisterSpec> newRegs,
565                                 HashSet<SsaInsn> deletedInsns) {
566         int index;
567         int length = newRegs.size();
568         SsaInsn next;
569         RegisterSpecList sources;
570         RegisterSpec source, result;
571         CstLiteralBits indexReg;
572 
573         switch (use.getOpcode().getOpcode()) {
574             case RegOps.AGET:
575                 // Replace array gets with moves
576                 next = getMoveForInsn(use);
577                 sources = use.getSources();
578                 indexReg = ((CstLiteralBits) sources.get(1).getTypeBearer());
579                 index = indexReg.getIntBits();
580                 if (index < length) {
581                     source = newRegs.get(index);
582                     result = source.withReg(next.getResult().getReg());
583                     insertPlainInsnBefore(next, RegisterSpecList.make(source),
584                                               result, RegOps.MOVE, null);
585                 } else {
586                     // Throw an exception if the index is out of bounds
587                     insertExceptionThrow(next, sources.get(1), deletedInsns);
588                     deletedInsns.add(next.getBlock().getInsns().get(2));
589                 }
590                 deletedInsns.add(next);
591                 break;
592             case RegOps.APUT:
593                 // Replace array puts with moves
594                 sources = use.getSources();
595                 indexReg = ((CstLiteralBits) sources.get(2).getTypeBearer());
596                 index = indexReg.getIntBits();
597                 if (index < length) {
598                     source = sources.get(0);
599                     result = source.withReg(newRegs.get(index).getReg());
600                     insertPlainInsnBefore(use, RegisterSpecList.make(source),
601                                               result, RegOps.MOVE, null);
602                     // Update the newReg entry to mark value as unknown now
603                     newRegs.set(index, result.withSimpleType());
604                 } else {
605                     // Throw an exception if the index is out of bounds
606                     insertExceptionThrow(use, sources.get(2), deletedInsns);
607                 }
608                 break;
609             case RegOps.ARRAY_LENGTH:
610                 // Replace array lengths with const instructions
611                 TypeBearer lengthReg = prev.getSources().get(0).getTypeBearer();
612                 //CstInteger lengthReg = CstInteger.make(length);
613                 next = getMoveForInsn(use);
614                 insertPlainInsnBefore(next, RegisterSpecList.EMPTY,
615                                           next.getResult(), RegOps.CONST,
616                                           (Constant) lengthReg);
617                 deletedInsns.add(next);
618                 break;
619             case RegOps.MARK_LOCAL:
620                 // Remove mark local instructions
621                 break;
622             case RegOps.FILL_ARRAY_DATA:
623                 // Create const instructions for each fill value
624                 Insn ropUse = use.getOriginalRopInsn();
625                 FillArrayDataInsn fill = (FillArrayDataInsn) ropUse;
626                 ArrayList<Constant> constList = fill.getInitValues();
627                 for (int i = 0; i < length; i++) {
628                     RegisterSpec newFill =
629                         RegisterSpec.make(newRegs.get(i).getReg(),
630                                               (TypeBearer) constList.get(i));
631                     insertPlainInsnBefore(use, RegisterSpecList.EMPTY, newFill,
632                                               RegOps.CONST, constList.get(i));
633                     // Update the newRegs to hold the new const value
634                     newRegs.set(i, newFill);
635                 }
636                 break;
637             default:
638         }
639     }
640 
641     /**
642      * Identifies extra moves added by scalar replacement and propagates the
643      * source of the move to any users of the result.
644      */
movePropagate()645     private void movePropagate() {
646         for (int i = 0; i < ssaMeth.getRegCount(); i++) {
647             SsaInsn insn = ssaMeth.getDefinitionForRegister(i);
648 
649             // Look for move instructions only
650             if (insn == null || insn.getOpcode() == null ||
651                 insn.getOpcode().getOpcode() != RegOps.MOVE) {
652                 continue;
653             }
654 
655             final ArrayList<SsaInsn>[] useList = ssaMeth.getUseListCopy();
656             final RegisterSpec source = insn.getSources().get(0);
657             final RegisterSpec result = insn.getResult();
658 
659             // Ignore moves that weren't added due to scalar replacement
660             if (source.getReg() < regCount && result.getReg() < regCount) {
661                 continue;
662             }
663 
664             // Create a mapping from source to result
665             RegisterMapper mapper = new RegisterMapper() {
666                 @Override
667                 public int getNewRegisterCount() {
668                     return ssaMeth.getRegCount();
669                 }
670 
671                 @Override
672                 public RegisterSpec map(RegisterSpec registerSpec) {
673                     if (registerSpec.getReg() == result.getReg()) {
674                         return source;
675                     }
676 
677                     return registerSpec;
678                 }
679             };
680 
681             // Modify all uses of the move to use the source of the move instead
682             for (SsaInsn use : useList[result.getReg()]) {
683                 use.mapSourceRegisters(mapper);
684             }
685         }
686     }
687 
688     /**
689      * Runs escape analysis and scalar replacement of arrays.
690      */
run()691     private void run() {
692         ssaMeth.forEachBlockDepthFirstDom(new SsaBasicBlock.Visitor() {
693             public void visitBlock (SsaBasicBlock block,
694                     SsaBasicBlock unused) {
695                 block.forEachInsn(new SsaInsn.Visitor() {
696                     public void visitMoveInsn(NormalSsaInsn insn) {
697                         // do nothing
698                     }
699 
700                     public void visitPhiInsn(PhiInsn insn) {
701                         // do nothing
702                     }
703 
704                     public void visitNonMoveInsn(NormalSsaInsn insn) {
705                         processInsn(insn);
706                     }
707                 });
708             }
709         });
710 
711         // Go through lattice and promote fieldSets as necessary
712         for (EscapeSet e : latticeValues) {
713             if (e.escape != EscapeState.NONE) {
714                 for (EscapeSet field : e.childSets) {
715                     if (e.escape.compareTo(field.escape) > 0) {
716                         field.escape = e.escape;
717                     }
718                 }
719             }
720         }
721 
722         // Perform scalar replacement for arrays
723         scalarReplacement();
724     }
725 
726     /**
727      * Replaces instructions that trigger an ArrayIndexOutofBounds exception
728      * with an actual throw of the exception.
729      *
730      * @param insn {@code non-null;} instruction causing the exception
731      * @param index {@code non-null;} index value that is out of bounds
732      * @param deletedInsns {@code non-null;} set of instructions marked for
733      * deletion
734      */
insertExceptionThrow(SsaInsn insn, RegisterSpec index, HashSet<SsaInsn> deletedInsns)735     private void insertExceptionThrow(SsaInsn insn, RegisterSpec index,
736                                           HashSet<SsaInsn> deletedInsns) {
737         // Create a new ArrayIndexOutOfBoundsException
738         CstType exception =
739             new CstType(Exceptions.TYPE_ArrayIndexOutOfBoundsException);
740         insertThrowingInsnBefore(insn, RegisterSpecList.EMPTY, null,
741                                      RegOps.NEW_INSTANCE, exception);
742 
743         // Add a successor block with a move result pseudo for the exception
744         SsaBasicBlock currBlock = insn.getBlock();
745         SsaBasicBlock newBlock =
746             currBlock.insertNewSuccessor(currBlock.getPrimarySuccessor());
747         SsaInsn newInsn = newBlock.getInsns().get(0);
748         RegisterSpec newReg =
749             RegisterSpec.make(ssaMeth.makeNewSsaReg(), exception);
750         insertPlainInsnBefore(newInsn, RegisterSpecList.EMPTY, newReg,
751                                   RegOps.MOVE_RESULT_PSEUDO, null);
752 
753         // Add another successor block to initialize the exception
754         SsaBasicBlock newBlock2 =
755             newBlock.insertNewSuccessor(newBlock.getPrimarySuccessor());
756         SsaInsn newInsn2 = newBlock2.getInsns().get(0);
757         CstNat newNat = new CstNat(new CstString("<init>"), new CstString("(I)V"));
758         CstMethodRef newRef = new CstMethodRef(exception, newNat);
759         insertThrowingInsnBefore(newInsn2, RegisterSpecList.make(newReg, index),
760                                      null, RegOps.INVOKE_DIRECT, newRef);
761         deletedInsns.add(newInsn2);
762 
763         // Add another successor block to throw the new exception
764         SsaBasicBlock newBlock3 =
765             newBlock2.insertNewSuccessor(newBlock2.getPrimarySuccessor());
766         SsaInsn newInsn3 = newBlock3.getInsns().get(0);
767         insertThrowingInsnBefore(newInsn3, RegisterSpecList.make(newReg), null,
768                                      RegOps.THROW, null);
769         newBlock3.replaceSuccessor(newBlock3.getPrimarySuccessorIndex(),
770                                        ssaMeth.getExitBlock().getIndex());
771         deletedInsns.add(newInsn3);
772     }
773 
774     /**
775      * Inserts a new PlainInsn before the given instruction.
776      * TODO: move this somewhere more appropriate
777      *
778      * @param insn {@code non-null;} instruction to insert before
779      * @param newSources {@code non-null;} sources of new instruction
780      * @param newResult {@code non-null;} result of new instruction
781      * @param newOpcode opcode of new instruction
782      * @param cst {@code null-ok;} constant for new instruction, if any
783      */
insertPlainInsnBefore(SsaInsn insn, RegisterSpecList newSources, RegisterSpec newResult, int newOpcode, Constant cst)784     private void insertPlainInsnBefore(SsaInsn insn,
785         RegisterSpecList newSources, RegisterSpec newResult, int newOpcode,
786         Constant cst) {
787 
788         Insn originalRopInsn = insn.getOriginalRopInsn();
789         Rop newRop;
790         if (newOpcode == RegOps.MOVE_RESULT_PSEUDO) {
791             newRop = Rops.opMoveResultPseudo(newResult.getType());
792         } else {
793             newRop = Rops.ropFor(newOpcode, newResult, newSources, cst);
794         }
795 
796         Insn newRopInsn;
797         if (cst == null) {
798             newRopInsn = new PlainInsn(newRop,
799                     originalRopInsn.getPosition(), newResult, newSources);
800         } else {
801             newRopInsn = new PlainCstInsn(newRop,
802                 originalRopInsn.getPosition(), newResult, newSources, cst);
803         }
804 
805         NormalSsaInsn newInsn = new NormalSsaInsn(newRopInsn, insn.getBlock());
806         List<SsaInsn> insns = insn.getBlock().getInsns();
807 
808         insns.add(insns.lastIndexOf(insn), newInsn);
809         ssaMeth.onInsnAdded(newInsn);
810     }
811 
812     /**
813      * Inserts a new ThrowingInsn before the given instruction.
814      * TODO: move this somewhere more appropriate
815      *
816      * @param insn {@code non-null;} instruction to insert before
817      * @param newSources {@code non-null;} sources of new instruction
818      * @param newResult {@code non-null;} result of new instruction
819      * @param newOpcode opcode of new instruction
820      * @param cst {@code null-ok;} constant for new instruction, if any
821      */
insertThrowingInsnBefore(SsaInsn insn, RegisterSpecList newSources, RegisterSpec newResult, int newOpcode, Constant cst)822     private void insertThrowingInsnBefore(SsaInsn insn,
823         RegisterSpecList newSources, RegisterSpec newResult, int newOpcode,
824         Constant cst) {
825 
826         Insn origRopInsn = insn.getOriginalRopInsn();
827         Rop newRop = Rops.ropFor(newOpcode, newResult, newSources, cst);
828         Insn newRopInsn;
829         if (cst == null) {
830             newRopInsn = new ThrowingInsn(newRop,
831                 origRopInsn.getPosition(), newSources, StdTypeList.EMPTY);
832         } else {
833             newRopInsn = new ThrowingCstInsn(newRop,
834                 origRopInsn.getPosition(), newSources, StdTypeList.EMPTY, cst);
835         }
836 
837         NormalSsaInsn newInsn = new NormalSsaInsn(newRopInsn, insn.getBlock());
838         List<SsaInsn> insns = insn.getBlock().getInsns();
839 
840         insns.add(insns.lastIndexOf(insn), newInsn);
841         ssaMeth.onInsnAdded(newInsn);
842     }
843 }
844