1 // Copyright (c) 2017, 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 5 package com.android.tools.r8.ir.code; 6 7 import com.android.tools.r8.graph.DexType; 8 import java.util.ArrayList; 9 import java.util.List; 10 import java.util.ListIterator; 11 import java.util.function.Predicate; 12 13 public interface InstructionListIterator extends ListIterator<Instruction> { 14 15 /** 16 * Peek the previous instruction. 17 * 18 * @return what will be returned by calling {@link #previous}. If there is no previous instruction 19 * <code>null</code> is returned. 20 */ peekPrevious()21 default Instruction peekPrevious() { 22 Instruction previous = null; 23 if (hasPrevious()) { 24 previous = previous(); 25 next(); 26 } 27 return previous; 28 } 29 30 /** 31 * Peek the next instruction. 32 * 33 * @return what will be returned by calling {@link #next}. If there is no next instruction 34 * <code>null</code> is returned. 35 */ peekNext()36 default Instruction peekNext() { 37 Instruction next = null; 38 if (hasNext()) { 39 next = next(); 40 previous(); 41 } 42 return next; 43 } 44 45 /** 46 * Continue to call {@link #next} while {@code predicate} tests {@code false}. 47 * 48 * @returns the instruction that matched the predicate or {@code null} if all instructions fails 49 * the predicate test 50 */ nextUntil(Predicate<Instruction> predicate)51 default Instruction nextUntil(Predicate<Instruction> predicate) { 52 while (hasNext()) { 53 Instruction instruction = next(); 54 if (predicate.test(instruction)) { 55 return instruction; 56 } 57 } 58 return null; 59 } 60 61 /** 62 * Remove the current instruction (aka the {@link Instruction} returned by the previous call to 63 * {@link #next}) without updating its def/use chains. 64 * <p> 65 * This is useful for instance when moving an instruction to another block that still dominates 66 * all its uses. In order to do that you would detach the instruction from the original 67 * block and add it to the new block. 68 */ detach()69 void detach(); 70 71 /** 72 * Replace the current instruction (aka the {@link Instruction} returned by the previous call to 73 * {@link #next} with the passed in <code>newInstruction</code>. 74 * 75 * The current instruction will be completely detached from the instruction stream with uses 76 * of its in-values removed. 77 * 78 * If the current instruction produces an out-value the new instruction must also produce 79 * an out-value, and all uses of the current instructions out-value will be replaced by the 80 * new instructions out-value. 81 * 82 * @param newInstruction the instruction to insert instead of the current. 83 */ replaceCurrentInstruction(Instruction newInstruction)84 void replaceCurrentInstruction(Instruction newInstruction); 85 86 /** 87 * Split the block into two blocks at the point of the {@link ListIterator} cursor. The existing 88 * block will have all the instructions before the cursor, and the new block all the 89 * instructions after the cursor. 90 * 91 * If the current block has catch handlers these catch handlers will be attached to the block 92 * containing the throwing instruction after the split. 93 * 94 * @param code the IR code for the block this iterator originates from. 95 * @param blockIterator basic block iterator used to iterate the blocks. This must be positioned 96 * just after the block for which this is the instruction iterator. After this method returns it 97 * will be positioned just after the basic block returned. 98 * @return Returns the new block with the instructions after the cursor. 99 */ split(IRCode code, ListIterator<BasicBlock> blockIterator)100 BasicBlock split(IRCode code, ListIterator<BasicBlock> blockIterator); 101 102 split(IRCode code)103 default BasicBlock split(IRCode code) { 104 return split(code, null); 105 } 106 107 /** 108 * Split the block into three blocks. The first split is at the point of the {@link ListIterator} 109 * cursor and the second split is <code>instructions</code> after the cursor. The existing 110 * block will have all the instructions before the cursor, and the two new blocks all the 111 * instructions after the cursor. 112 * 113 * If the current block have catch handlers these catch handlers will be attached to the block 114 * containing the throwing instruction after the split. 115 * 116 * @param instructions the number of instructions to include in the second block. 117 * @param code the IR code for the block this iterator originates from. 118 * @param blockIterator basic block iterator used to iterate the blocks. This must be positioned 119 * just after the block for this is the instruction iterator. After this method returns it will be 120 * positioned just after the second block inserted. That is after the successor of the block 121 * returned. 122 * @return Returns the new block with the instructions right after the cursor. 123 */ 124 // TODO(sgjesse): Refactor to avoid the need for passing code and blockIterator. split(int instructions, IRCode code, ListIterator<BasicBlock> blockIterator)125 BasicBlock split(int instructions, IRCode code, ListIterator<BasicBlock> blockIterator); 126 127 /** 128 * See {@link #split(int, IRCode, ListIterator)}. 129 */ split(int instructions, IRCode code)130 default BasicBlock split(int instructions, IRCode code) { 131 return split(instructions, code, null); 132 } 133 134 /** 135 * Inline the code in {@code inlinee} into {@code code}, replacing the invoke instruction at the 136 * position after the cursor. 137 * 138 * The instruction at the position after cursor must be an invoke that matches the signature for 139 * the code in {@code inlinee}. 140 * 141 * With one exception (see below) both the calling code and the inlinee can have catch handlers. 142 * 143 * <strong>EXCEPTION:</strong> If the invoke instruction is covered by catch handlers, and the 144 * code for {@code inlinee} always throws (does not have a normal return) inlining is currently 145 * <strong>NOT</strong> supported. 146 * 147 * @param code the IR code for the block this iterator originates from. 148 * @param inlinee the IR code for the block this iterator originates from. 149 * @param blockIterator basic block iterator used to iterate the blocks. This must be positioned 150 * just after the block for which this is the instruction iterator. After this method returns it 151 * will be positioned just after the basic block returned. 152 * @param blocksToRemove list passed where blocks that where detached from the graph, but not 153 * removed are added. When inlining an inlinee that always throws blocks in the <code>code</code> 154 * can be detached, and not simply removed unsing the passed <code>blockIterator</code>. When 155 * iterating using <code>blockIterator</code> after then method returns the blocks in this list 156 * must be skipped when iterating with the active <code>blockIterator</code> and ultimately 157 * removed. 158 * @param downcast tells the inliner to issue a check cast opertion. 159 * @return the basic block with the instructions right after the inlining. This can be a block 160 * which can also be in the <code>blocksToRemove</code> list. 161 */ 162 // TODO(sgjesse): Refactor to avoid the need for passing code. 163 // TODO(sgjesse): Refactor to avoid the need for passing blocksToRemove. 164 // TODO(sgjesse): Maybe don't return a BasicBlock, as it can be in blocksToRemove. 165 // TODO(sgjesse): Maybe find a better place for this method. 166 // TODO(sgjesse): Support inlinee with throwing instructions for invokes with existing handlers. inlineInvoke(IRCode code, IRCode inlinee, ListIterator<BasicBlock> blockIterator, List<BasicBlock> blocksToRemove, DexType downcast)167 BasicBlock inlineInvoke(IRCode code, IRCode inlinee, ListIterator<BasicBlock> blockIterator, 168 List<BasicBlock> blocksToRemove, DexType downcast); 169 170 /** 171 * See {@link #inlineInvoke(IRCode, IRCode, ListIterator<BasicBlock>, List<BasicBlock>, DexType)}. 172 */ inlineInvoke(IRCode code, IRCode inlinee)173 default BasicBlock inlineInvoke(IRCode code, IRCode inlinee) { 174 List<BasicBlock> blocksToRemove = new ArrayList<>(); 175 BasicBlock result = inlineInvoke(code, inlinee, null, blocksToRemove, null); 176 code.removeBlocks(blocksToRemove); 177 return result; 178 } 179 } 180