• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 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.CstInsn;
20 import com.android.dx.rop.code.Insn;
21 import com.android.dx.rop.code.PlainInsn;
22 import com.android.dx.rop.code.RegOps;
23 import com.android.dx.rop.code.RegisterSpecList;
24 import com.android.dx.rop.code.Rop;
25 import com.android.dx.rop.code.RegisterSpec;
26 import com.android.dx.rop.code.Rops;
27 import com.android.dx.rop.cst.Constant;
28 import com.android.dx.rop.cst.CstInteger;
29 import com.android.dx.rop.cst.TypedConstant;
30 import com.android.dx.rop.type.TypeBearer;
31 import com.android.dx.rop.type.Type;
32 
33 import java.util.ArrayList;
34 import java.util.BitSet;
35 
36 /**
37  * A small variant of Wegman and Zadeck's Sparse Conditional Constant
38  * Propagation algorithm.
39  */
40 public class SCCP {
41     /** Lattice values  */
42     private static final int TOP = 0;
43     private static final int CONSTANT = 1;
44     private static final int VARYING = 2;
45     /** method we're processing */
46     private SsaMethod ssaMeth;
47     /** ssaMeth.getRegCount() */
48     private int regCount;
49     /** Lattice values for each SSA register */
50     private int[] latticeValues;
51     /** For those registers that are constant, this is the constant value */
52     private Constant[] latticeConstants;
53     /** Worklist of basic blocks to be processed */
54     private ArrayList<SsaBasicBlock> cfgWorklist;
55     /** Worklist of executed basic blocks with phis to be processed */
56     private ArrayList<SsaBasicBlock> cfgPhiWorklist;
57     /** Bitset containing bits for each block that has been found executable */
58     private BitSet executableBlocks;
59     /** Worklist for SSA edges.  This is a list of registers to process */
60     private ArrayList<SsaInsn> ssaWorklist;
61     /**
62      * Worklist for SSA edges that represent varying values.  It makes the
63      * algorithm much faster if you move all values to VARYING as fast as
64      * possible.
65      */
66     private ArrayList<SsaInsn> varyingWorklist;
67     /** Worklist of potential branches to convert to gotos */
68     private ArrayList<SsaInsn> branchWorklist;
69 
SCCP(SsaMethod ssaMeth)70     private SCCP(SsaMethod ssaMeth) {
71         this.ssaMeth = ssaMeth;
72         this.regCount = ssaMeth.getRegCount();
73         this.latticeValues = new int[this.regCount];
74         this.latticeConstants = new Constant[this.regCount];
75         this.cfgWorklist = new ArrayList<SsaBasicBlock>();
76         this.cfgPhiWorklist = new ArrayList<SsaBasicBlock>();
77         this.executableBlocks = new BitSet(ssaMeth.getBlocks().size());
78         this.ssaWorklist = new ArrayList<SsaInsn>();
79         this.varyingWorklist = new ArrayList<SsaInsn>();
80         this.branchWorklist = new ArrayList<SsaInsn>();
81         for (int i = 0; i < this.regCount; i++) {
82             latticeValues[i] = TOP;
83             latticeConstants[i] = null;
84         }
85     }
86 
87     /**
88      * Performs sparse conditional constant propagation on a method.
89      * @param ssaMethod Method to process
90      */
process(SsaMethod ssaMethod)91     public static void process (SsaMethod ssaMethod) {
92         new SCCP(ssaMethod).run();
93     }
94 
95     /**
96      * Adds a SSA basic block to the CFG worklist if it's unexecuted, or
97      * to the CFG phi worklist if it's already executed.
98      * @param ssaBlock Block to add
99      */
addBlockToWorklist(SsaBasicBlock ssaBlock)100     private void addBlockToWorklist(SsaBasicBlock ssaBlock) {
101         if (!executableBlocks.get(ssaBlock.getIndex())) {
102             cfgWorklist.add(ssaBlock);
103             executableBlocks.set(ssaBlock.getIndex());
104         } else {
105             cfgPhiWorklist.add(ssaBlock);
106         }
107     }
108 
109     /**
110      * Adds an SSA register's uses to the SSA worklist.
111      * @param reg SSA register
112      * @param latticeValue new lattice value for @param reg.
113      */
addUsersToWorklist(int reg, int latticeValue)114     private void addUsersToWorklist(int reg, int latticeValue) {
115         if (latticeValue == VARYING) {
116             for (SsaInsn insn : ssaMeth.getUseListForRegister(reg)) {
117                 varyingWorklist.add(insn);
118             }
119         } else {
120             for (SsaInsn insn : ssaMeth.getUseListForRegister(reg)) {
121                 ssaWorklist.add(insn);
122             }
123         }
124     }
125 
126     /**
127      * Sets a lattice value for a register to value.
128      * @param reg SSA register
129      * @param value Lattice value
130      * @param cst Constant value (may be null)
131      * @return true if the lattice value changed.
132      */
setLatticeValueTo(int reg, int value, Constant cst)133     private boolean setLatticeValueTo(int reg, int value, Constant cst) {
134         if (value != CONSTANT) {
135             if (latticeValues[reg] != value) {
136                 latticeValues[reg] = value;
137                 return true;
138             }
139             return false;
140         } else {
141             if (latticeValues[reg] != value
142                     || !latticeConstants[reg].equals(cst)) {
143                 latticeValues[reg] = value;
144                 latticeConstants[reg] = cst;
145                 return true;
146             }
147             return false;
148         }
149     }
150 
151     /**
152      * Simulates a PHI node and set the lattice for the result
153      * to the appropriate value.
154      * Meet values:
155      * TOP x anything = TOP
156      * VARYING x anything = VARYING
157      * CONSTANT x CONSTANT = CONSTANT if equal constants, VARYING otherwise
158      * @param insn PHI to simulate.
159      */
simulatePhi(PhiInsn insn)160     private void simulatePhi(PhiInsn insn) {
161         int phiResultReg = insn.getResult().getReg();
162 
163         if (latticeValues[phiResultReg] == VARYING) {
164             return;
165         }
166 
167         RegisterSpecList sources = insn.getSources();
168         int phiResultValue = TOP;
169         Constant phiConstant = null;
170         int sourceSize = sources.size();
171 
172         for (int i = 0; i < sourceSize; i++) {
173             int predBlockIndex = insn.predBlockIndexForSourcesIndex(i);
174             int sourceReg = sources.get(i).getReg();
175             int sourceRegValue = latticeValues[sourceReg];
176 
177             if (!executableBlocks.get(predBlockIndex)) {
178                 continue;
179             }
180 
181             if (sourceRegValue == CONSTANT) {
182                 if (phiConstant == null) {
183                     phiConstant = latticeConstants[sourceReg];
184                     phiResultValue = CONSTANT;
185                  } else if (!latticeConstants[sourceReg].equals(phiConstant)){
186                     phiResultValue = VARYING;
187                     break;
188                 }
189             } else {
190                 phiResultValue = sourceRegValue;
191                 break;
192             }
193         }
194         if (setLatticeValueTo(phiResultReg, phiResultValue, phiConstant)) {
195             addUsersToWorklist(phiResultReg, phiResultValue);
196         }
197     }
198 
199     /**
200      * Simulate a block and note the results in the lattice.
201      * @param block Block to visit
202      */
simulateBlock(SsaBasicBlock block)203     private void simulateBlock(SsaBasicBlock block) {
204         for (SsaInsn insn : block.getInsns()) {
205             if (insn instanceof PhiInsn) {
206                 simulatePhi((PhiInsn) insn);
207             } else {
208                 simulateStmt(insn);
209             }
210         }
211     }
212 
213     /**
214      * Simulate the phis in a block and note the results in the lattice.
215      * @param block Block to visit
216      */
simulatePhiBlock(SsaBasicBlock block)217     private void simulatePhiBlock(SsaBasicBlock block) {
218         for (SsaInsn insn : block.getInsns()) {
219             if (insn instanceof PhiInsn) {
220                 simulatePhi((PhiInsn) insn);
221             } else {
222                 return;
223             }
224         }
225     }
226 
latticeValName(int latticeVal)227     private static String latticeValName(int latticeVal) {
228         switch (latticeVal) {
229             case TOP: return "TOP";
230             case CONSTANT: return "CONSTANT";
231             case VARYING: return "VARYING";
232             default: return "UNKNOWN";
233         }
234     }
235 
236     /**
237      * Simulates branch insns, if possible. Adds reachable successor blocks
238      * to the CFG worklists.
239      * @param insn branch to simulate
240      */
simulateBranch(SsaInsn insn)241     private void simulateBranch(SsaInsn insn) {
242         Rop opcode = insn.getOpcode();
243         RegisterSpecList sources = insn.getSources();
244 
245         boolean constantBranch = false;
246         boolean constantSuccessor = false;
247 
248         // Check if the insn is a branch with a constant condition
249         if (opcode.getBranchingness() == Rop.BRANCH_IF) {
250             Constant cA = null;
251             Constant cB = null;
252 
253             RegisterSpec specA = sources.get(0);
254             int regA = specA.getReg();
255             if (!ssaMeth.isRegALocal(specA) &&
256                     latticeValues[regA] == CONSTANT) {
257                 cA = latticeConstants[regA];
258             }
259 
260             if (sources.size() == 2) {
261                 RegisterSpec specB = sources.get(1);
262                 int regB = specB.getReg();
263                 if (!ssaMeth.isRegALocal(specB) &&
264                         latticeValues[regB] == CONSTANT) {
265                     cB = latticeConstants[regB];
266                 }
267             }
268 
269             // Calculate the result of the condition
270             if (cA != null && sources.size() == 1) {
271                 switch (((TypedConstant) cA).getBasicType()) {
272                     case Type.BT_INT:
273                         constantBranch = true;
274                         int vA = ((CstInteger) cA).getValue();
275                         switch (opcode.getOpcode()) {
276                             case RegOps.IF_EQ:
277                                 constantSuccessor = (vA == 0);
278                                 break;
279                             case RegOps.IF_NE:
280                                 constantSuccessor = (vA != 0);
281                                 break;
282                             case RegOps.IF_LT:
283                                 constantSuccessor = (vA < 0);
284                                 break;
285                             case RegOps.IF_GE:
286                                 constantSuccessor = (vA >= 0);
287                                 break;
288                             case RegOps.IF_LE:
289                                 constantSuccessor = (vA <= 0);
290                                 break;
291                             case RegOps.IF_GT:
292                                 constantSuccessor = (vA > 0);
293                                 break;
294                             default:
295                                 throw new RuntimeException("Unexpected op");
296                         }
297                         break;
298                     default:
299                         // not yet supported
300                 }
301             } else if (cA != null && cB != null) {
302                 switch (((TypedConstant) cA).getBasicType()) {
303                     case Type.BT_INT:
304                         constantBranch = true;
305                         int vA = ((CstInteger) cA).getValue();
306                         int vB = ((CstInteger) cB).getValue();
307                         switch (opcode.getOpcode()) {
308                             case RegOps.IF_EQ:
309                                 constantSuccessor = (vA == vB);
310                                 break;
311                             case RegOps.IF_NE:
312                                 constantSuccessor = (vA != vB);
313                                 break;
314                             case RegOps.IF_LT:
315                                 constantSuccessor = (vA < vB);
316                                 break;
317                             case RegOps.IF_GE:
318                                 constantSuccessor = (vA >= vB);
319                                 break;
320                             case RegOps.IF_LE:
321                                 constantSuccessor = (vA <= vB);
322                                 break;
323                             case RegOps.IF_GT:
324                                 constantSuccessor = (vA > vB);
325                                 break;
326                             default:
327                                 throw new RuntimeException("Unexpected op");
328                         }
329                         break;
330                     default:
331                         // not yet supported
332                 }
333             }
334         }
335 
336         /*
337          * If condition is constant, add only the target block to the
338          * worklist. Otherwise, add all successors to the worklist.
339          */
340         SsaBasicBlock block = insn.getBlock();
341 
342         if (constantBranch) {
343             int successorBlock;
344             if (constantSuccessor) {
345                 successorBlock = block.getSuccessorList().get(1);
346             } else {
347                 successorBlock = block.getSuccessorList().get(0);
348             }
349             addBlockToWorklist(ssaMeth.getBlocks().get(successorBlock));
350             branchWorklist.add(insn);
351         } else {
352             for (int i = 0; i < block.getSuccessorList().size(); i++) {
353                 int successorBlock = block.getSuccessorList().get(i);
354                 addBlockToWorklist(ssaMeth.getBlocks().get(successorBlock));
355             }
356         }
357     }
358 
359     /**
360      * Simulates math insns, if possible.
361      *
362      * @param insn non-null insn to simulate
363      * @param resultType basic type of the result
364      * @return constant result or null if not simulatable.
365      */
simulateMath(SsaInsn insn, int resultType)366     private Constant simulateMath(SsaInsn insn, int resultType) {
367         Insn ropInsn = insn.getOriginalRopInsn();
368         int opcode = insn.getOpcode().getOpcode();
369         RegisterSpecList sources = insn.getSources();
370         int regA = sources.get(0).getReg();
371         Constant cA;
372         Constant cB;
373 
374         if (latticeValues[regA] != CONSTANT) {
375             cA = null;
376         } else {
377             cA = latticeConstants[regA];
378         }
379 
380         if (sources.size() == 1) {
381             CstInsn cstInsn = (CstInsn) ropInsn;
382             cB = cstInsn.getConstant();
383         } else { /* sources.size() == 2 */
384             int regB = sources.get(1).getReg();
385             if (latticeValues[regB] != CONSTANT) {
386                 cB = null;
387             } else {
388                 cB = latticeConstants[regB];
389             }
390         }
391 
392         if (cA == null || cB == null) {
393             //TODO handle a constant of 0 with MUL or AND
394             return null;
395         }
396 
397         switch (resultType) {
398             case Type.BT_INT:
399                 int vR;
400                 boolean skip=false;
401 
402                 int vA = ((CstInteger) cA).getValue();
403                 int vB = ((CstInteger) cB).getValue();
404 
405                 switch (opcode) {
406                     case RegOps.ADD:
407                         vR = vA + vB;
408                         break;
409                     case RegOps.SUB:
410                         // 1 source for reverse sub, 2 sources for regular sub
411                         if (sources.size() == 1) {
412                             vR = vB - vA;
413                         } else {
414                             vR = vA - vB;
415                         }
416                         break;
417                     case RegOps.MUL:
418                         vR = vA * vB;
419                         break;
420                     case RegOps.DIV:
421                         if (vB == 0) {
422                             skip = true;
423                             vR = 0; // just to hide a warning
424                         } else {
425                             vR = vA / vB;
426                         }
427                         break;
428                     case RegOps.AND:
429                         vR = vA & vB;
430                         break;
431                     case RegOps.OR:
432                         vR = vA | vB;
433                         break;
434                     case RegOps.XOR:
435                         vR = vA ^ vB;
436                         break;
437                     case RegOps.SHL:
438                         vR = vA << vB;
439                         break;
440                     case RegOps.SHR:
441                         vR = vA >> vB;
442                         break;
443                     case RegOps.USHR:
444                         vR = vA >>> vB;
445                         break;
446                     case RegOps.REM:
447                         if (vB == 0) {
448                             skip = true;
449                             vR = 0; // just to hide a warning
450                         } else {
451                             vR = vA % vB;
452                         }
453                         break;
454                     default:
455                         throw new RuntimeException("Unexpected op");
456                 }
457 
458                 return skip ? null : CstInteger.make(vR);
459 
460             default:
461                 // not yet supported
462                 return null;
463         }
464     }
465 
466     /**
467      * Simulates a statement and set the result lattice value.
468      * @param insn instruction to simulate
469      */
simulateStmt(SsaInsn insn)470     private void simulateStmt(SsaInsn insn) {
471         Insn ropInsn = insn.getOriginalRopInsn();
472         if (ropInsn.getOpcode().getBranchingness() != Rop.BRANCH_NONE
473                 || ropInsn.getOpcode().isCallLike()) {
474             simulateBranch(insn);
475         }
476 
477         int opcode = insn.getOpcode().getOpcode();
478         RegisterSpec result = insn.getResult();
479 
480         if (result == null) {
481             // Find move-result-pseudo result for int div and int rem
482             if (opcode == RegOps.DIV || opcode == RegOps.REM) {
483                 SsaBasicBlock succ = insn.getBlock().getPrimarySuccessor();
484                 result = succ.getInsns().get(0).getResult();
485             } else {
486                 return;
487             }
488         }
489 
490         int resultReg = result.getReg();
491         int resultValue = VARYING;
492         Constant resultConstant = null;
493 
494         switch (opcode) {
495             case RegOps.CONST: {
496                 CstInsn cstInsn = (CstInsn)ropInsn;
497                 resultValue = CONSTANT;
498                 resultConstant = cstInsn.getConstant();
499                 break;
500             }
501             case RegOps.MOVE: {
502                 if (insn.getSources().size() == 1) {
503                     int sourceReg = insn.getSources().get(0).getReg();
504                     resultValue = latticeValues[sourceReg];
505                     resultConstant = latticeConstants[sourceReg];
506                 }
507                 break;
508             }
509             case RegOps.ADD:
510             case RegOps.SUB:
511             case RegOps.MUL:
512             case RegOps.DIV:
513             case RegOps.AND:
514             case RegOps.OR:
515             case RegOps.XOR:
516             case RegOps.SHL:
517             case RegOps.SHR:
518             case RegOps.USHR:
519             case RegOps.REM: {
520                 resultConstant = simulateMath(insn, result.getBasicType());
521                 if (resultConstant != null) {
522                     resultValue = CONSTANT;
523                 }
524                 break;
525             }
526             case RegOps.MOVE_RESULT_PSEUDO: {
527                 if (latticeValues[resultReg] == CONSTANT) {
528                     resultValue = latticeValues[resultReg];
529                     resultConstant = latticeConstants[resultReg];
530                 }
531                 break;
532             }
533             // TODO: Handle non-int arithmetic.
534             // TODO: Eliminate check casts that we can prove the type of.
535             default: {}
536         }
537         if (setLatticeValueTo(resultReg, resultValue, resultConstant)) {
538             addUsersToWorklist(resultReg, resultValue);
539         }
540     }
541 
run()542     private void run() {
543         SsaBasicBlock firstBlock = ssaMeth.getEntryBlock();
544         addBlockToWorklist(firstBlock);
545 
546         /* Empty all the worklists by propagating our values */
547         while (!cfgWorklist.isEmpty()
548                 || !cfgPhiWorklist.isEmpty()
549                 || !ssaWorklist.isEmpty()
550                 || !varyingWorklist.isEmpty()) {
551             while (!cfgWorklist.isEmpty()) {
552                 int listSize = cfgWorklist.size() - 1;
553                 SsaBasicBlock block = cfgWorklist.remove(listSize);
554                 simulateBlock(block);
555             }
556 
557             while (!cfgPhiWorklist.isEmpty()) {
558                 int listSize = cfgPhiWorklist.size() - 1;
559                 SsaBasicBlock block = cfgPhiWorklist.remove(listSize);
560                 simulatePhiBlock(block);
561             }
562 
563             while (!varyingWorklist.isEmpty()) {
564                 int listSize = varyingWorklist.size() - 1;
565                 SsaInsn insn = varyingWorklist.remove(listSize);
566 
567                 if (!executableBlocks.get(insn.getBlock().getIndex())) {
568                     continue;
569                 }
570 
571                 if (insn instanceof PhiInsn) {
572                     simulatePhi((PhiInsn)insn);
573                 } else {
574                     simulateStmt(insn);
575                 }
576             }
577             while (!ssaWorklist.isEmpty()) {
578                 int listSize = ssaWorklist.size() - 1;
579                 SsaInsn insn = ssaWorklist.remove(listSize);
580 
581                 if (!executableBlocks.get(insn.getBlock().getIndex())) {
582                     continue;
583                 }
584 
585                 if (insn instanceof PhiInsn) {
586                     simulatePhi((PhiInsn)insn);
587                 } else {
588                     simulateStmt(insn);
589                 }
590             }
591         }
592 
593         replaceConstants();
594         replaceBranches();
595     }
596 
597     /**
598      * Replaces TypeBearers in source register specs with constant type
599      * bearers if possible. These are then referenced in later optimization
600      * steps.
601      */
replaceConstants()602     private void replaceConstants() {
603         for (int reg = 0; reg < regCount; reg++) {
604             if (latticeValues[reg] != CONSTANT) {
605                 continue;
606             }
607             if (!(latticeConstants[reg] instanceof TypedConstant)) {
608                 // We can't do much with these
609                 continue;
610             }
611 
612             SsaInsn defn = ssaMeth.getDefinitionForRegister(reg);
613             TypeBearer typeBearer = defn.getResult().getTypeBearer();
614 
615             if (typeBearer.isConstant()) {
616                 /*
617                  * The definition was a constant already.
618                  * The uses should be as well.
619                  */
620                 continue;
621             }
622 
623             // Update the destination RegisterSpec with the constant value
624             RegisterSpec dest = defn.getResult();
625             RegisterSpec newDest
626                     = dest.withType((TypedConstant)latticeConstants[reg]);
627             defn.setResult(newDest);
628 
629             /*
630              * Update the sources RegisterSpec's of all non-move uses.
631              * These will be used in later steps.
632              */
633             for (SsaInsn insn : ssaMeth.getUseListForRegister(reg)) {
634                 if (insn.isPhiOrMove()) {
635                     continue;
636                 }
637 
638                 NormalSsaInsn nInsn = (NormalSsaInsn) insn;
639                 RegisterSpecList sources = insn.getSources();
640 
641                 int index = sources.indexOfRegister(reg);
642 
643                 RegisterSpec spec = sources.get(index);
644                 RegisterSpec newSpec
645                         = spec.withType((TypedConstant)latticeConstants[reg]);
646 
647                 nInsn.changeOneSource(index, newSpec);
648             }
649         }
650     }
651 
652     /**
653      * Replaces branches that have constant conditions with gotos
654      */
replaceBranches()655     private void replaceBranches() {
656         for (SsaInsn insn : branchWorklist) {
657             // Find if a successor block is never executed
658             int oldSuccessor = -1;
659             SsaBasicBlock block = insn.getBlock();
660             int successorSize = block.getSuccessorList().size();
661             for (int i = 0; i < successorSize; i++) {
662                 int successorBlock = block.getSuccessorList().get(i);
663                 if (!executableBlocks.get(successorBlock)) {
664                     oldSuccessor = successorBlock;
665                 }
666             }
667 
668             /*
669              * Prune branches that have already been handled and ones that no
670              * longer have constant conditions (no nonexecutable successors)
671              */
672             if (successorSize != 2 || oldSuccessor == -1) continue;
673 
674             // Replace branch with goto
675             Insn originalRopInsn = insn.getOriginalRopInsn();
676             block.replaceLastInsn(new PlainInsn(Rops.GOTO,
677                 originalRopInsn.getPosition(), null, RegisterSpecList.EMPTY));
678             block.removeSuccessor(oldSuccessor);
679         }
680     }
681 }
682