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.cf.code.Merger; 20 import com.android.dx.rop.code.LocalItem; 21 import com.android.dx.rop.code.RegisterSpec; 22 import com.android.dx.rop.code.RegisterSpecList; 23 import com.android.dx.rop.type.Type; 24 import com.android.dx.rop.type.TypeBearer; 25 import java.util.BitSet; 26 import java.util.List; 27 28 /** 29 * Resolves the result types of phi instructions. When phi instructions 30 * are inserted, their result types are set to BT_VOID (which is a nonsensical 31 * type for a register) but must be resolve to a real type before converting 32 * out of SSA form.<p> 33 * 34 * The resolve is done as an iterative merge of each phi's operand types. 35 * Phi operands may be themselves be the result of unresolved phis, 36 * and the algorithm tries to find the most-fit type (for example, if every 37 * operand is the same constant value or the same local variable info, we want 38 * that to be reflected).<p> 39 * 40 * This algorithm assumes a dead-code remover has already removed all 41 * circular-only phis that may have been inserted. 42 */ 43 public class PhiTypeResolver { 44 45 SsaMethod ssaMeth; 46 /** indexed by register; all registers still defined by unresolved phis */ 47 private final BitSet worklist; 48 49 /** 50 * Resolves all phi types in the method 51 * @param ssaMeth method to process 52 */ 53 public static void process (SsaMethod ssaMeth) { 54 new PhiTypeResolver(ssaMeth).run(); 55 } 56 57 private PhiTypeResolver(SsaMethod ssaMeth) { 58 this.ssaMeth = ssaMeth; 59 worklist = new BitSet(ssaMeth.getRegCount()); 60 } 61 62 /** 63 * Runs the phi-type resolver. 64 */ 65 private void run() { 66 67 int regCount = ssaMeth.getRegCount(); 68 69 for (int reg = 0; reg < regCount; reg++) { 70 SsaInsn definsn = ssaMeth.getDefinitionForRegister(reg); 71 72 if (definsn != null 73 && (definsn.getResult().getBasicType() == Type.BT_VOID)) { 74 worklist.set(reg); 75 } 76 } 77 78 int reg; 79 while ( 0 <= (reg = worklist.nextSetBit(0))) { 80 worklist.clear(reg); 81 82 /* 83 * definitions on the worklist have a type of BT_VOID, which 84 * must have originated from a PhiInsn. 85 */ 86 PhiInsn definsn = (PhiInsn)ssaMeth.getDefinitionForRegister(reg); 87 88 if (resolveResultType(definsn)) { 89 /* 90 * If the result type has changed, re-resolve all phis 91 * that use this. 92 */ 93 94 List<SsaInsn> useList = ssaMeth.getUseListForRegister(reg); 95 96 int sz = useList.size(); 97 for (int i = 0; i < sz; i++ ) { 98 SsaInsn useInsn = useList.get(i); 99 RegisterSpec resultReg = useInsn.getResult(); 100 if (resultReg != null && useInsn instanceof PhiInsn) { 101 worklist.set(resultReg.getReg()); 102 } 103 } 104 } 105 } 106 } 107 108 /** 109 * Returns true if a and b are equal, whether 110 * or not either of them are null. 111 * @param a 112 * @param b 113 * @return true if equal 114 */ 115 private static boolean equalsHandlesNulls(LocalItem a, LocalItem b) { 116 return (a == b) || ((a != null) && a.equals(b)); 117 } 118 119 /** 120 * Resolves the result of a phi insn based on its operands. The "void" 121 * type, which is a nonsensical type for a register, is used for 122 * registers defined by as-of-yet-unresolved phi operations. 123 * 124 * @return true if the result type changed, false if no change 125 */ 126 boolean resolveResultType(PhiInsn insn) { 127 insn.updateSourcesToDefinitions(ssaMeth); 128 129 RegisterSpecList sources = insn.getSources(); 130 131 // Start by finding the first non-void operand 132 RegisterSpec first = null; 133 int firstIndex = -1; 134 135 int szSources = sources.size(); 136 for (int i = 0 ; i <szSources ; i++) { 137 RegisterSpec rs = sources.get(i); 138 139 if (rs.getBasicType() != Type.BT_VOID) { 140 first = rs; 141 firstIndex = i; 142 } 143 } 144 145 if (first == null) { 146 // All operands are void -- we're not ready to resolve yet 147 return false; 148 } 149 150 LocalItem firstLocal = first.getLocalItem(); 151 TypeBearer mergedType = first.getType(); 152 boolean sameLocals = true; 153 for (int i = 0 ; i < szSources ; i++) { 154 if (i == firstIndex) { 155 continue; 156 } 157 158 RegisterSpec rs = sources.get(i); 159 160 // Just skip void (unresolved phi results) for now 161 if (rs.getBasicType() == Type.BT_VOID){ 162 continue; 163 } 164 165 sameLocals = sameLocals 166 && equalsHandlesNulls(firstLocal, rs.getLocalItem()); 167 168 mergedType = Merger.mergeType(mergedType, rs.getType()); 169 } 170 171 TypeBearer newResultType; 172 173 if (mergedType != null) { 174 newResultType = mergedType; 175 } else { 176 StringBuilder sb = new StringBuilder(); 177 178 for (int i = 0; i < szSources; i++) { 179 sb.append(sources.get(i).toString()); 180 sb.append(' '); 181 } 182 183 throw new RuntimeException ("Couldn't map types in phi insn:" + sb); 184 } 185 186 LocalItem newLocal = sameLocals ? firstLocal : null; 187 188 RegisterSpec result = insn.getResult(); 189 190 if ((result.getTypeBearer() == newResultType) 191 && equalsHandlesNulls(newLocal, result.getLocalItem())) { 192 return false; 193 } 194 195 insn.changeResultType(newResultType, newLocal); 196 197 return true; 198 } 199 } 200