1 // Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file 2 // for details. All rights reserved. Use of this source code is governed by a 3 // BSD-style license that can be found in the LICENSE file. 4 package com.android.tools.r8.ir.code; 5 6 import com.android.tools.r8.graph.DexEncodedMethod; 7 import com.android.tools.r8.graph.DexItemFactory; 8 import com.android.tools.r8.graph.DexType; 9 import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator; 10 import com.android.tools.r8.utils.CfgPrinter; 11 import java.util.ArrayList; 12 import java.util.Collections; 13 import java.util.Comparator; 14 import java.util.HashSet; 15 import java.util.Iterator; 16 import java.util.LinkedList; 17 import java.util.List; 18 import java.util.ListIterator; 19 import java.util.Set; 20 import java.util.stream.Collectors; 21 22 public class IRCode { 23 24 public final DexEncodedMethod method; 25 26 public LinkedList<BasicBlock> blocks; 27 public final ValueNumberGenerator valueNumberGenerator; 28 29 private BasicBlock normalExitBlock; 30 private boolean numbered = false; 31 private int nextInstructionNumber = 0; 32 IRCode( DexEncodedMethod method, LinkedList<BasicBlock> blocks, BasicBlock normalExitBlock, ValueNumberGenerator valueNumberGenerator)33 public IRCode( 34 DexEncodedMethod method, 35 LinkedList<BasicBlock> blocks, 36 BasicBlock normalExitBlock, 37 ValueNumberGenerator valueNumberGenerator) { 38 this.method = method; 39 this.blocks = blocks; 40 this.normalExitBlock = normalExitBlock; 41 this.valueNumberGenerator = valueNumberGenerator; 42 } 43 ensureBlockNumbering()44 private void ensureBlockNumbering() { 45 if (!numbered) { 46 numbered = true; 47 BasicBlock[] sorted = topologicallySortedBlocks(); 48 for (int i = 0; i < sorted.length; i++) { 49 sorted[i].setNumber(i); 50 } 51 } 52 } 53 54 @Override toString()55 public String toString() { 56 ensureBlockNumbering(); 57 StringBuilder builder = new StringBuilder(); 58 builder.append("blocks:\n"); 59 for (BasicBlock block : blocks) { 60 builder.append(block.toDetailedString()); 61 builder.append("\n"); 62 } 63 return builder.toString(); 64 } 65 clearMarks()66 public void clearMarks() { 67 for (BasicBlock block : blocks) { 68 block.clearMark(); 69 } 70 } 71 removeMarkedBlocks()72 public void removeMarkedBlocks() { 73 ListIterator<BasicBlock> blockIterator = listIterator(); 74 while (blockIterator.hasNext()) { 75 BasicBlock block = blockIterator.next(); 76 if (block.isMarked()) { 77 blockIterator.remove(); 78 if (block == normalExitBlock) { 79 normalExitBlock = null; 80 } 81 } 82 } 83 } 84 removeBlocks(List<BasicBlock> blocksToRemove)85 public void removeBlocks(List<BasicBlock> blocksToRemove) { 86 blocks.removeAll(blocksToRemove); 87 if (blocksToRemove.contains(normalExitBlock)) { 88 normalExitBlock = null; 89 } 90 } 91 92 /** 93 * Compute quasi topologically sorted list of the basic blocks using depth first search. 94 * 95 * TODO(ager): We probably want to compute strongly connected components and topologically 96 * sort strongly connected components instead. However, this is much better than having 97 * no sorting. 98 */ topologicallySortedBlocks()99 public BasicBlock[] topologicallySortedBlocks() { 100 return topologicallySortedBlocks(Collections.emptyList()); 101 } 102 topologicallySortedBlocks(List<BasicBlock> blocksToIgnore)103 public BasicBlock[] topologicallySortedBlocks(List<BasicBlock> blocksToIgnore) { 104 clearMarks(); 105 int reachableBlocks = blocks.size() - blocksToIgnore.size(); 106 BasicBlock[] sorted = new BasicBlock[reachableBlocks]; 107 BasicBlock entryBlock = blocks.getFirst(); 108 int index = depthFirstSorting(entryBlock, sorted, reachableBlocks - 1); 109 assert index == -1; 110 return sorted; 111 } 112 depthFirstSorting(BasicBlock block, BasicBlock[] sorted, int index)113 private int depthFirstSorting(BasicBlock block, BasicBlock[] sorted, int index) { 114 if (!block.isMarked()) { 115 block.mark(); 116 for (BasicBlock succ : block.getSuccessors()) { 117 index = depthFirstSorting(succ, sorted, index); 118 } 119 assert sorted[index] == null; 120 sorted[index] = block; 121 return index - 1; 122 } 123 return index; 124 } 125 print(CfgPrinter printer)126 public void print(CfgPrinter printer) { 127 ensureBlockNumbering(); 128 for (BasicBlock block : blocks) { 129 block.print(printer); 130 } 131 } 132 isConsistentSSA()133 public boolean isConsistentSSA() { 134 assert isConsistentGraph() && consistentDefUseChains() && validThrowingInstructions(); 135 return true; 136 } 137 isConsistentGraph()138 public boolean isConsistentGraph() { 139 assert consistentBlockNumbering(); 140 assert consistentPredecessorSuccessors(); 141 assert consistentCatchHandlers(); 142 assert consistentBlockInstructions(); 143 assert normalExitBlock == null || normalExitBlock.exit().isReturn(); 144 return true; 145 } 146 consistentDefUseChains()147 private boolean consistentDefUseChains() { 148 Set<Value> values = new HashSet<>(); 149 150 for (BasicBlock block : blocks) { 151 int predecessorCount = block.getPredecessors().size(); 152 // Check that all phi uses are consistent. 153 for (Phi phi : block.getPhis()) { 154 assert phi.getOperands().size() == predecessorCount; 155 values.add(phi); 156 for (Value value : phi.getOperands()) { 157 values.add(value); 158 if (value.isPhi()) { 159 Phi phiOperand = value.asPhi(); 160 assert phiOperand.getBlock().getPhis().contains(phiOperand); 161 assert phiOperand.uniquePhiUsers().contains(phi); 162 } else { 163 Instruction definition = value.definition; 164 assert definition.outValue() == value; 165 } 166 } 167 } 168 for (Instruction instruction : block.getInstructions()) { 169 assert instruction.getBlock() == block; 170 Value outValue = instruction.outValue(); 171 if (outValue != null) { 172 values.add(outValue); 173 assert outValue.definition == instruction; 174 Value previousLocalValue = outValue.getPreviousLocalValue(); 175 if (previousLocalValue != null) { 176 values.add(previousLocalValue); 177 assert previousLocalValue.debugUsers().contains(instruction); 178 } 179 } 180 for (Value value : instruction.inValues()) { 181 values.add(value); 182 assert value.uniqueUsers().contains(instruction); 183 if (value.isPhi()) { 184 Phi phi = value.asPhi(); 185 assert phi.getBlock().getPhis().contains(phi); 186 } else { 187 Instruction definition = value.definition; 188 assert definition.outValue() == value; 189 } 190 } 191 } 192 } 193 194 for (Value value : values) { 195 assert consistentValueUses(value); 196 } 197 198 return true; 199 } 200 consistentValueUses(Value value)201 private boolean consistentValueUses(Value value) { 202 for (Instruction user : value.uniqueUsers()) { 203 assert user.inValues().contains(value); 204 } 205 for (Phi phiUser : value.uniquePhiUsers()) { 206 assert phiUser.getOperands().contains(value); 207 assert phiUser.getBlock().getPhis().contains(phiUser); 208 } 209 if (value.debugUsers() != null) { 210 for (Instruction debugUser : value.debugUsers()) { 211 assert debugUser.getPreviousLocalValue() == value 212 || debugUser.getDebugValues().contains(value); 213 } 214 } 215 return true; 216 } 217 consistentPredecessorSuccessors()218 private boolean consistentPredecessorSuccessors() { 219 for (BasicBlock block : blocks) { 220 // Check that all successors are distinct. 221 assert new HashSet<>(block.getSuccessors()).size() == block.getSuccessors().size(); 222 for (BasicBlock succ : block.getSuccessors()) { 223 // Check that successors are in the block list. 224 assert blocks.contains(succ); 225 // Check that successors have this block as a predecessor. 226 assert succ.getPredecessors().contains(block); 227 } 228 // Check that all predecessors are distinct. 229 assert new HashSet<>(block.getPredecessors()).size() == block.getPredecessors().size(); 230 for (BasicBlock pred : block.getPredecessors()) { 231 // Check that predecessors are in the block list. 232 assert blocks.contains(pred); 233 // Check that predecessors have this block as a successor. 234 assert pred.getSuccessors().contains(block); 235 } 236 } 237 return true; 238 } 239 consistentCatchHandlers()240 private boolean consistentCatchHandlers() { 241 for (BasicBlock block : blocks) { 242 // Check that catch handlers are always the first successors of a block. 243 if (block.hasCatchHandlers()) { 244 assert block.exit().isGoto() || block.exit().isThrow(); 245 CatchHandlers<Integer> catchHandlers = block.getCatchHandlersWithSuccessorIndexes(); 246 // If there is a catch-all guard it must be the last. 247 List<DexType> guards = catchHandlers.getGuards(); 248 int lastGuardIndex = guards.size() - 1; 249 for (int i = 0; i < guards.size(); i++) { 250 assert guards.get(i) != DexItemFactory.catchAllType || i == lastGuardIndex; 251 } 252 // Check that all successors except maybe the last are catch successors. 253 List<Integer> sortedHandlerIndices = new ArrayList<>(catchHandlers.getAllTargets()); 254 sortedHandlerIndices.sort(Comparator.naturalOrder()); 255 int firstIndex = sortedHandlerIndices.get(0); 256 int lastIndex = sortedHandlerIndices.get(sortedHandlerIndices.size() - 1); 257 assert firstIndex == 0; 258 assert lastIndex < sortedHandlerIndices.size(); 259 int lastSuccessorIndex = block.getSuccessors().size() - 1; 260 assert lastIndex == lastSuccessorIndex // All successors are catch successors. 261 || lastIndex == lastSuccessorIndex - 1; // All but one successors are catch successors. 262 assert lastIndex == lastSuccessorIndex || !block.exit().isThrow(); 263 } 264 } 265 return true; 266 } 267 268 public boolean consistentBlockNumbering() { 269 return blocks.stream() 270 .collect(Collectors.groupingBy(BasicBlock::getNumber, Collectors.counting())) 271 .entrySet().stream().noneMatch((bb2count) -> bb2count.getValue() > 1); 272 } 273 consistentBlockInstructions()274 private boolean consistentBlockInstructions() { 275 for (BasicBlock block : blocks) { 276 for (Instruction instruction : block.getInstructions()) { 277 assert instruction.getBlock() == block; 278 } 279 } 280 return true; 281 } 282 validThrowingInstructions()283 private boolean validThrowingInstructions() { 284 for (BasicBlock block : blocks) { 285 if (block.hasCatchHandlers()) { 286 boolean seenThrowing = false; 287 for (Instruction instruction : block.getInstructions()) { 288 if (instruction.instructionTypeCanThrow()) { 289 assert !seenThrowing; 290 seenThrowing = true; 291 continue; 292 } 293 // After the throwing instruction only debug instructions an the final jump 294 // instruction is allowed. 295 // TODO(ager): For now allow const instructions due to the way consts are pushed 296 // towards their use 297 if (seenThrowing) { 298 assert instruction.isDebugInstruction() 299 || instruction.isJumpInstruction() 300 || instruction.isConstInstruction() 301 || instruction.isNewArrayFilledData(); 302 } 303 } 304 } 305 } 306 return true; 307 } 308 instructionIterator()309 public InstructionIterator instructionIterator() { 310 return new IRCodeInstructionsIterator(this); 311 } 312 setNormalExitBlock(BasicBlock block)313 void setNormalExitBlock(BasicBlock block) { 314 normalExitBlock = block; 315 } 316 getNormalExitBlock()317 public BasicBlock getNormalExitBlock() { 318 return normalExitBlock; 319 } 320 listIterator()321 public ListIterator<BasicBlock> listIterator() { 322 return new BasicBlockIterator(this); 323 } 324 listIterator(int index)325 public ListIterator<BasicBlock> listIterator(int index) { 326 return new BasicBlockIterator(this, index); 327 } 328 numberInstructions()329 public BasicBlock[] numberInstructions() { 330 BasicBlock[] blocks = topologicallySortedBlocks(); 331 for (BasicBlock block : blocks) { 332 for (Instruction instruction : block.getInstructions()) { 333 instruction.setNumber(nextInstructionNumber); 334 nextInstructionNumber += LinearScanRegisterAllocator.INSTRUCTION_NUMBER_DELTA; 335 } 336 } 337 return blocks; 338 } 339 numberRemainingInstructions()340 public int numberRemainingInstructions() { 341 InstructionIterator it = instructionIterator(); 342 while (it.hasNext()) { 343 Instruction i = it.next(); 344 if (i.getNumber() == -1) { 345 i.setNumber(nextInstructionNumber); 346 nextInstructionNumber += LinearScanRegisterAllocator.INSTRUCTION_NUMBER_DELTA; 347 } 348 } 349 return nextInstructionNumber; 350 } 351 getNextInstructionNumber()352 public int getNextInstructionNumber() { 353 return nextInstructionNumber; 354 } 355 collectArguments()356 public List<Value> collectArguments() { 357 final List<Value> arguments = new ArrayList<>(); 358 Iterator<Instruction> iterator = blocks.get(0).iterator(); 359 while (iterator.hasNext()) { 360 Instruction instruction = iterator.next(); 361 if (instruction.isArgument()) { 362 arguments.add(instruction.asArgument().outValue()); 363 } 364 } 365 assert arguments.size() 366 == method.method.proto.parameters.values.length + (method.accessFlags.isStatic() ? 0 : 1); 367 return arguments; 368 } 369 createValue(MoveType moveType, Value.DebugInfo debugInfo)370 public Value createValue(MoveType moveType, Value.DebugInfo debugInfo) { 371 return new Value(valueNumberGenerator.next(), moveType, debugInfo); 372 } 373 createValue(MoveType moveType)374 public Value createValue(MoveType moveType) { 375 return createValue(moveType, null); 376 } 377 createIntConstant(int value)378 public ConstNumber createIntConstant(int value) { 379 return new ConstNumber(ConstType.INT, createValue(MoveType.SINGLE), value); 380 } 381 createTrue()382 public ConstNumber createTrue() { 383 return new ConstNumber(ConstType.INT, createValue(MoveType.SINGLE), 1); 384 } 385 createFalse()386 public ConstNumber createFalse() { 387 return new ConstNumber(ConstType.INT, createValue(MoveType.SINGLE), 0); 388 } 389 getHighestBlockNumber()390 public final int getHighestBlockNumber() { 391 return blocks.stream().max(Comparator.comparingInt(BasicBlock::getNumber)).get().getNumber(); 392 } 393 } 394