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