PhiInsn.java revision 4b4413ab3d8de5805276cfcde3d7f535d9f64e85
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 17package com.android.dx.ssa; 18 19import com.android.dx.rop.code.*; 20import com.android.dx.rop.type.Type; 21import com.android.dx.rop.type.TypeBearer; 22import com.android.dx.util.Hex; 23 24import java.util.ArrayList; 25import java.util.List; 26 27/** 28 * A Phi instruction (magical post-control-flow-merge) instruction 29 * in SSA form. Will be converted to moves in predecessor blocks before 30 * conversion back to ROP form. 31 */ 32public final class PhiInsn extends SsaInsn { 33 /** 34 * result register. The original result register of the phi insn 35 * is needed during the renaming process after the new result 36 * register has already been chosen. 37 */ 38 private final int ropResultReg; 39 40 /** 41 * {@code non-null;} operands of the instruction; built up by 42 * {@link #addPhiOperand} 43 */ 44 private final ArrayList<Operand> operands = new ArrayList<Operand>(); 45 46 /** {@code null-ok;} source registers; constructed lazily */ 47 private RegisterSpecList sources; 48 49 /** 50 * Constructs a new phi insn with no operands. 51 * 52 * @param resultReg the result reg for this phi insn 53 * @param block block containing this insn. 54 */ 55 public PhiInsn(RegisterSpec resultReg, SsaBasicBlock block) { 56 super(resultReg, block); 57 ropResultReg = resultReg.getReg(); 58 } 59 60 /** 61 * Makes a phi insn with a void result type. 62 * 63 * @param resultReg the result register for this phi insn. 64 * @param block block containing this insn. 65 */ 66 public PhiInsn(final int resultReg, final SsaBasicBlock block) { 67 /* 68 * The result type here is bogus: The type depends on the 69 * operand and will be derived later. 70 */ 71 super(RegisterSpec.make(resultReg, Type.VOID), block); 72 ropResultReg = resultReg; 73 } 74 75 /** {@inheritDoc} */ 76 public PhiInsn clone() { 77 throw new UnsupportedOperationException("can't clone phi"); 78 } 79 80 /** 81 * Updates the TypeBearers of all the sources (phi operands) to be 82 * the current TypeBearer of the register-defining instruction's result. 83 * This is used during phi-type resolution.<p> 84 * 85 * Note that local association of operands are preserved in this step. 86 * 87 * @param ssaMeth method that contains this insn 88 */ 89 public void updateSourcesToDefinitions(SsaMethod ssaMeth) { 90 for (Operand o : operands) { 91 RegisterSpec def 92 = ssaMeth.getDefinitionForRegister( 93 o.regSpec.getReg()).getResult(); 94 95 o.regSpec = o.regSpec.withType(def.getType()); 96 } 97 98 sources = null; 99 } 100 101 /** 102 * Changes the result type. Used during phi type resolution 103 * 104 * @param type {@code non-null;} new TypeBearer 105 * @param local {@code null-ok;} new local info, if available 106 */ 107 public void changeResultType(TypeBearer type, LocalItem local) { 108 setResult(RegisterSpec.makeLocalOptional( 109 getResult().getReg(), type, local)); 110 } 111 112 /** 113 * Gets the original rop-form result reg. This is useful during renaming. 114 * 115 * @return the original rop-form result reg 116 */ 117 public int getRopResultReg() { 118 return ropResultReg; 119 } 120 121 /** 122 * Adds an operand to this phi instruction. 123 * 124 * @param registerSpec register spec, including type and reg of operand 125 * @param predBlock predecessor block to be associated with this operand 126 */ 127 public void addPhiOperand(RegisterSpec registerSpec, 128 SsaBasicBlock predBlock) { 129 operands.add(new Operand(registerSpec, predBlock.getIndex(), 130 predBlock.getRopLabel())); 131 132 // Un-cache sources, in case someone has already called getSources(). 133 sources = null; 134 } 135 136 /** 137 * Gets the index of the pred block associated with the RegisterSpec 138 * at the particular getSources() index. 139 * 140 * @param sourcesIndex index of source in getSources() 141 * @return block index 142 */ 143 public int predBlockIndexForSourcesIndex(int sourcesIndex) { 144 return operands.get(sourcesIndex).blockIndex; 145 } 146 147 /** 148 * {@inheritDoc} 149 * 150 * Always returns null for {@code PhiInsn}s. 151 */ 152 @Override 153 public Rop getOpcode() { 154 return null; 155 } 156 157 /** 158 * {@inheritDoc} 159 * 160 * Always returns null for {@code PhiInsn}s. 161 */ 162 @Override 163 public Insn getOriginalRopInsn() { 164 return null; 165 } 166 167 /** 168 * {@inheritDoc} 169 * 170 * Always returns false for {@code PhiInsn}s. 171 */ 172 @Override 173 public boolean canThrow() { 174 return false; 175 } 176 177 /** 178 * Gets sources. Constructed lazily from phi operand data structures and 179 * then cached. 180 * 181 * @return {@code non-null;} sources list 182 */ 183 public RegisterSpecList getSources() { 184 if (sources != null) { 185 return sources; 186 } 187 188 if (operands.size() == 0) { 189 // How'd this happen? A phi insn with no operand? 190 return RegisterSpecList.EMPTY; 191 } 192 193 int szSources = operands.size(); 194 sources = new RegisterSpecList(szSources); 195 196 for (int i = 0; i < szSources; i++) { 197 Operand o = operands.get(i); 198 199 sources.set(i, o.regSpec); 200 } 201 202 sources.setImmutable(); 203 return sources; 204 } 205 206 /** {@inheritDoc} */ 207 @Override 208 public boolean isRegASource(int reg) { 209 /* 210 * Avoid creating a sources list in case it has not already been 211 * created. 212 */ 213 214 for (Operand o : operands) { 215 if (o.regSpec.getReg() == reg) { 216 return true; 217 } 218 } 219 220 return false; 221 } 222 223 /** 224 * @return true if all operands use the same register 225 */ 226 public boolean areAllOperandsEqual() { 227 if (operands.size() == 0 ) { 228 // This should never happen. 229 return true; 230 } 231 232 int firstReg = operands.get(0).regSpec.getReg(); 233 for (Operand o : operands) { 234 if (firstReg != o.regSpec.getReg()) { 235 return false; 236 } 237 } 238 239 return true; 240 } 241 242 /** {@inheritDoc} */ 243 @Override 244 public final void mapSourceRegisters(RegisterMapper mapper) { 245 for (Operand o : operands) { 246 RegisterSpec old = o.regSpec; 247 o.regSpec = mapper.map(old); 248 if (old != o.regSpec) { 249 getBlock().getParent().onSourceChanged(this, old, o.regSpec); 250 } 251 } 252 sources = null; 253 } 254 255 /** 256 * Always throws an exeption, since a phi insn may not be 257 * converted back to rop form. 258 * 259 * @return always throws exception 260 */ 261 @Override 262 public Insn toRopInsn() { 263 throw new IllegalArgumentException( 264 "Cannot convert phi insns to rop form"); 265 } 266 267 /** 268 * Returns the list of predecessor blocks associated with all operands 269 * that have {@code reg} as an operand register. 270 * 271 * @param reg register to look up 272 * @param ssaMeth method we're operating on 273 * @return list of predecessor blocks, empty if none 274 */ 275 public List<SsaBasicBlock> predBlocksForReg(int reg, SsaMethod ssaMeth) { 276 ArrayList<SsaBasicBlock> ret = new ArrayList<SsaBasicBlock>(); 277 278 for (Operand o : operands) { 279 if (o.regSpec.getReg() == reg) { 280 ret.add(ssaMeth.getBlocks().get(o.blockIndex)); 281 } 282 } 283 284 return ret; 285 } 286 287 /** {@inheritDoc} */ 288 @Override 289 public boolean isPhiOrMove() { 290 return true; 291 } 292 293 /** {@inheritDoc} */ 294 @Override 295 public boolean hasSideEffect() { 296 return Optimizer.getPreserveLocals() && getLocalAssignment() != null; 297 } 298 299 /** {@inheritDoc} */ 300 @Override 301 public void accept(SsaInsn.Visitor v) { 302 v.visitPhiInsn(this); 303 } 304 305 /** {@inheritDoc} */ 306 public String toHuman() { 307 return toHumanWithInline(null); 308 } 309 310 /** 311 * Returns human-readable string for listing dumps. This method 312 * allows sub-classes to specify extra text. 313 * 314 * @param extra {@code null-ok;} the argument to print after the opcode 315 * @return human-readable string for listing dumps 316 */ 317 protected final String toHumanWithInline(String extra) { 318 StringBuffer sb = new StringBuffer(80); 319 320 sb.append(SourcePosition.NO_INFO); 321 sb.append(": phi"); 322 323 if (extra != null) { 324 sb.append("("); 325 sb.append(extra); 326 sb.append(")"); 327 } 328 329 RegisterSpec result = getResult(); 330 331 if (result == null) { 332 sb.append(" ."); 333 } else { 334 sb.append(" "); 335 sb.append(result.toHuman()); 336 } 337 338 sb.append(" <-"); 339 340 int sz = getSources().size(); 341 if (sz == 0) { 342 sb.append(" ."); 343 } else { 344 for (int i = 0; i < sz; i++) { 345 sb.append(" "); 346 sb.append(sources.get(i).toHuman() 347 + "[b=" 348 + Hex.u2(operands.get(i).ropLabel) + "]"); 349 } 350 } 351 352 return sb.toString(); 353 } 354 355 /** 356 * A single phi operand, consiting of source register and block index 357 * for move. 358 */ 359 private static class Operand { 360 public RegisterSpec regSpec; 361 public final int blockIndex; 362 public final int ropLabel; // only used for debugging 363 364 public Operand(RegisterSpec regSpec, int blockIndex, int ropLabel) { 365 this.regSpec = regSpec; 366 this.blockIndex = blockIndex; 367 this.ropLabel = ropLabel; 368 } 369 } 370 371 /** 372 * Visitor interface for instances of this (outer) class. 373 */ 374 public static interface Visitor { 375 public void visitPhiInsn(PhiInsn insn); 376 } 377} 378