• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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