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