Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 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 | |
Christoffer Adamsen | 383bcd8 | 2018-04-26 14:23:31 +0200 | [diff] [blame] | 6 | import static com.android.tools.r8.ir.code.IRCode.INSTRUCTION_NUMBER_DELTA; |
Christoffer Quist Adamsen | b55c98e | 2024-01-22 15:26:00 +0100 | [diff] [blame] | 7 | import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer; |
Christoffer Quist Adamsen | 4111aba | 2024-01-23 15:35:55 +0100 | [diff] [blame] | 8 | import static com.google.common.base.Predicates.alwaysFalse; |
Christoffer Adamsen | 383bcd8 | 2018-04-26 14:23:31 +0200 | [diff] [blame] | 9 | |
Mads Ager | c52c353 | 2017-06-14 11:54:02 +0200 | [diff] [blame] | 10 | import com.android.tools.r8.errors.CompilationError; |
Christoffer Quist Adamsen | e77fd71 | 2021-06-18 10:57:03 +0200 | [diff] [blame] | 11 | import com.android.tools.r8.errors.Unreachable; |
Christoffer Quist Adamsen | 2e7d7e4 | 2019-03-12 15:43:07 +0100 | [diff] [blame] | 12 | import com.android.tools.r8.graph.AppView; |
Ian Zerny | 5d16b09 | 2017-07-06 10:48:15 +0200 | [diff] [blame] | 13 | import com.android.tools.r8.graph.DebugLocalInfo; |
Ian Zerny | d73c0b4 | 2017-09-07 07:38:42 +0200 | [diff] [blame] | 14 | import com.android.tools.r8.graph.DebugLocalInfo.PrintLevel; |
Christoffer Quist Adamsen | 95dd66b | 2018-06-11 09:12:45 +0200 | [diff] [blame] | 15 | import com.android.tools.r8.graph.DexItemFactory; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 16 | import com.android.tools.r8.graph.DexType; |
Morten Krogh-Jespersen | bb340d1 | 2022-08-30 12:23:01 +0200 | [diff] [blame] | 17 | import com.android.tools.r8.graph.ProgramMethod; |
| 18 | import com.android.tools.r8.graph.UseRegistry; |
Christoffer Quist Adamsen | c52a3ac | 2023-03-28 13:04:47 +0200 | [diff] [blame] | 19 | import com.android.tools.r8.graph.lens.GraphLens; |
Christoffer Quist Adamsen | 7e2a9d4 | 2023-03-28 13:05:04 +0200 | [diff] [blame] | 20 | import com.android.tools.r8.graph.lens.NonIdentityGraphLens; |
Morten Krogh-Jespersen | 88facd9 | 2020-09-22 14:51:27 +0200 | [diff] [blame] | 21 | import com.android.tools.r8.ir.analysis.VerifyTypesHelper; |
Jinseong Jeon | fd08600 | 2019-01-10 14:55:00 -0800 | [diff] [blame] | 22 | import com.android.tools.r8.ir.analysis.type.Nullability; |
Christoffer Quist Adamsen | e0302c2 | 2020-03-20 14:39:56 +0100 | [diff] [blame] | 23 | import com.android.tools.r8.ir.analysis.type.TypeElement; |
Ian Zerny | a30db87 | 2018-08-17 10:50:54 +0200 | [diff] [blame] | 24 | import com.android.tools.r8.ir.code.Phi.RegisterReadType; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 25 | import com.android.tools.r8.ir.conversion.DexBuilder; |
| 26 | import com.android.tools.r8.ir.conversion.IRBuilder; |
Christoffer Quist Adamsen | 4111aba | 2024-01-23 15:35:55 +0100 | [diff] [blame] | 27 | import com.android.tools.r8.ir.optimize.AffectedValues; |
Mads Ager | b83918c | 2019-01-10 12:20:45 +0100 | [diff] [blame] | 28 | import com.android.tools.r8.utils.InternalOptions; |
Christoffer Quist Adamsen | c406cdd | 2022-07-01 13:17:44 +0200 | [diff] [blame] | 29 | import com.android.tools.r8.utils.IterableUtils; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 30 | import com.android.tools.r8.utils.ListUtils; |
| 31 | import com.android.tools.r8.utils.StringUtils; |
Ian Zerny | 5d16b09 | 2017-07-06 10:48:15 +0200 | [diff] [blame] | 32 | import com.android.tools.r8.utils.StringUtils.BraceType; |
Christoffer Quist Adamsen | e1fa9de | 2022-04-22 12:56:21 +0200 | [diff] [blame] | 33 | import com.android.tools.r8.utils.TraversalContinuation; |
Christoffer Quist Adamsen | f927336 | 2022-06-16 15:14:06 +0200 | [diff] [blame] | 34 | import com.android.tools.r8.utils.TriFunction; |
mikaelpeltier | 99bca2f | 2017-12-22 14:49:17 +0100 | [diff] [blame] | 35 | import com.google.common.base.Equivalence; |
| 36 | import com.google.common.base.Equivalence.Wrapper; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 37 | import com.google.common.collect.ImmutableList; |
Søren Gjesse | 315c053 | 2018-10-01 15:36:11 +0200 | [diff] [blame] | 38 | import com.google.common.collect.ImmutableSet; |
Christoffer Quist Adamsen | b348955 | 2019-10-15 13:02:13 +0200 | [diff] [blame] | 39 | import com.google.common.collect.Sets; |
Ian Zerny | 5d16b09 | 2017-07-06 10:48:15 +0200 | [diff] [blame] | 40 | import it.unimi.dsi.fastutil.ints.Int2ReferenceMap; |
Christoffer Quist Adamsen | 95dd66b | 2018-06-11 09:12:45 +0200 | [diff] [blame] | 41 | import it.unimi.dsi.fastutil.ints.IntArrayList; |
| 42 | import it.unimi.dsi.fastutil.ints.IntList; |
Mikaël Peltier | e8b2b16 | 2017-11-08 08:52:25 +0100 | [diff] [blame] | 43 | import java.util.ArrayDeque; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 44 | import java.util.ArrayList; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 45 | import java.util.Collection; |
Stephan Herhut | 2ecfe14 | 2017-06-01 14:24:01 +0200 | [diff] [blame] | 46 | import java.util.Collections; |
Christoffer Quist Adamsen | 95dd66b | 2018-06-11 09:12:45 +0200 | [diff] [blame] | 47 | import java.util.Comparator; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 48 | import java.util.HashMap; |
mikaelpeltier | 99bca2f | 2017-12-22 14:49:17 +0100 | [diff] [blame] | 49 | import java.util.Iterator; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 50 | import java.util.List; |
| 51 | import java.util.ListIterator; |
| 52 | import java.util.Map; |
Mads Ager | ea9abac | 2017-06-02 14:44:20 +0200 | [diff] [blame] | 53 | import java.util.Map.Entry; |
Christoffer Quist Adamsen | 684e281 | 2019-10-07 13:49:05 +0200 | [diff] [blame] | 54 | import java.util.NoSuchElementException; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 55 | import java.util.Set; |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 56 | import java.util.WeakHashMap; |
Christoffer Quist Adamsen | e1fa9de | 2022-04-22 12:56:21 +0200 | [diff] [blame] | 57 | import java.util.function.BiFunction; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 58 | import java.util.function.Consumer; |
| 59 | import java.util.function.Function; |
Christoffer Quist Adamsen | c406cdd | 2022-07-01 13:17:44 +0200 | [diff] [blame] | 60 | import java.util.function.Predicate; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 61 | |
| 62 | /** |
| 63 | * Basic block abstraction. |
| 64 | */ |
| 65 | public class BasicBlock { |
| 66 | |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 67 | public interface BasicBlockChangeListener { |
| 68 | void onSuccessorsMayChange(BasicBlock block); |
| 69 | |
| 70 | void onPredecessorsMayChange(BasicBlock block); |
| 71 | } |
| 72 | |
Ian Zerny | 5d16b09 | 2017-07-06 10:48:15 +0200 | [diff] [blame] | 73 | private Int2ReferenceMap<DebugLocalInfo> localsAtEntry; |
| 74 | |
Christoffer Quist Adamsen | a015e37 | 2021-06-29 20:12:29 +0200 | [diff] [blame] | 75 | public boolean consistentBlockInstructions(boolean argumentsAllowed, boolean debug, boolean ssa) { |
Mathias Rav | 99446b0 | 2018-05-03 12:37:38 +0200 | [diff] [blame] | 76 | for (Instruction instruction : getInstructions()) { |
Ian Zerny | 0dba4dd | 2018-09-26 14:48:22 +0200 | [diff] [blame] | 77 | assert instruction.verifyValidPositionInfo(debug); |
Mathias Rav | 99446b0 | 2018-05-03 12:37:38 +0200 | [diff] [blame] | 78 | assert instruction.getBlock() == this; |
| 79 | assert !instruction.isArgument() || argumentsAllowed; |
| 80 | assert !instruction.isDebugLocalRead() || !instruction.getDebugValues().isEmpty(); |
Christoffer Quist Adamsen | a015e37 | 2021-06-29 20:12:29 +0200 | [diff] [blame] | 81 | assert !instruction.isInitClass() |
| 82 | || consistentInitClassInstruction(instruction.asInitClass(), ssa); |
Ian Zerny | 7271f71 | 2018-09-20 09:22:59 +0200 | [diff] [blame] | 83 | if (instruction.isMoveException()) { |
| 84 | assert instruction == entry(); |
| 85 | for (BasicBlock pred : getPredecessors()) { |
| 86 | assert pred.hasCatchSuccessor(this) |
| 87 | || (pred.isTrivialGoto() && pred.endOfGotoChain() == this); |
| 88 | } |
| 89 | } |
Christoffer Adamsen | 7c36896 | 2018-05-04 08:33:58 +0200 | [diff] [blame] | 90 | if (!instruction.isArgument()) { |
Mathias Rav | 99446b0 | 2018-05-03 12:37:38 +0200 | [diff] [blame] | 91 | argumentsAllowed = false; |
| 92 | } |
| 93 | } |
| 94 | return true; |
| 95 | } |
| 96 | |
Christoffer Quist Adamsen | a015e37 | 2021-06-29 20:12:29 +0200 | [diff] [blame] | 97 | public boolean consistentInitClassInstruction(InitClass initClass, boolean ssa) { |
| 98 | if (!ssa) { |
| 99 | return true; |
| 100 | } |
| 101 | assert initClass.hasOutValue(); |
| 102 | assert !initClass.outValue().hasDebugUsers(); |
| 103 | assert !initClass.outValue().hasPhiUsers(); |
| 104 | assert initClass.outValue().uniqueUsers().stream().allMatch(Instruction::isPop); |
| 105 | return true; |
| 106 | } |
| 107 | |
Christoffer Quist Adamsen | 56004fa | 2023-07-03 20:02:12 +0200 | [diff] [blame] | 108 | public boolean verifyTypes( |
| 109 | AppView<?> appView, ProgramMethod context, VerifyTypesHelper verifyTypesHelper) { |
Morten Krogh-Jespersen | 88facd9 | 2020-09-22 14:51:27 +0200 | [diff] [blame] | 110 | assert instructions.stream() |
Christoffer Quist Adamsen | 56004fa | 2023-07-03 20:02:12 +0200 | [diff] [blame] | 111 | .allMatch(instruction -> instruction.verifyTypes(appView, context, verifyTypesHelper)); |
Christoffer Quist Adamsen | 2767692 | 2018-10-08 10:57:05 +0200 | [diff] [blame] | 112 | return true; |
| 113 | } |
| 114 | |
Ian Zerny | 5d16b09 | 2017-07-06 10:48:15 +0200 | [diff] [blame] | 115 | public void setLocalsAtEntry(Int2ReferenceMap<DebugLocalInfo> localsAtEntry) { |
| 116 | this.localsAtEntry = localsAtEntry; |
| 117 | } |
| 118 | |
| 119 | public Int2ReferenceMap<DebugLocalInfo> getLocalsAtEntry() { |
| 120 | return localsAtEntry; |
| 121 | } |
| 122 | |
Christoffer Adamsen | fa3dc19 | 2025-03-18 08:18:10 +0000 | [diff] [blame] | 123 | public void replaceLastInstruction(Instruction instruction) { |
Andrew Grieve | ee12534 | 2024-11-14 12:20:39 -0500 | [diff] [blame] | 124 | InstructionListIterator iterator = listIterator(getInstructions().size()); |
Stephan Herhut | 3f026d3 | 2017-08-07 14:58:13 +0200 | [diff] [blame] | 125 | iterator.previous(); |
| 126 | iterator.replaceCurrentInstruction(instruction); |
| 127 | } |
| 128 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 129 | public enum ThrowingInfo { |
Christoffer Quist Adamsen | 26ef5a9 | 2019-02-21 17:03:31 +0100 | [diff] [blame] | 130 | NO_THROW, |
Christoffer Quist Adamsen | e1fa9de | 2022-04-22 12:56:21 +0200 | [diff] [blame] | 131 | CAN_THROW |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 132 | } |
| 133 | |
| 134 | public enum EdgeType { |
| 135 | NON_EDGE, |
| 136 | NORMAL, |
| 137 | EXCEPTIONAL |
| 138 | } |
| 139 | |
| 140 | public static class Pair implements Comparable<Pair> { |
| 141 | |
| 142 | public BasicBlock first; |
| 143 | public BasicBlock second; |
| 144 | |
| 145 | public Pair(BasicBlock first, BasicBlock second) { |
| 146 | this.first = first; |
| 147 | this.second = second; |
| 148 | } |
| 149 | |
| 150 | @Override |
| 151 | public int compareTo(Pair o) { |
| 152 | if (first != o.first) { |
| 153 | return first.getNumber() - o.first.getNumber(); |
| 154 | } |
| 155 | if (second != o.second) { |
| 156 | return second.getNumber() - o.second.getNumber(); |
| 157 | } |
| 158 | return 0; |
| 159 | } |
Mikaël Peltier | 427a73a | 2017-11-30 08:57:20 +0100 | [diff] [blame] | 160 | |
| 161 | @Override |
| 162 | public String toString() { |
| 163 | return "Edge: " + first.getNumber() + " -> " + second.getNumber(); |
| 164 | } |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 165 | } |
| 166 | |
| 167 | private final List<BasicBlock> successors = new ArrayList<>(); |
| 168 | private final List<BasicBlock> predecessors = new ArrayList<>(); |
| 169 | |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 170 | private Set<BasicBlockChangeListener> onControlFlowEdgesMayChangeListeners = null; |
| 171 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 172 | // Catch handler information about which successors are catch handlers and what their guards are. |
| 173 | private CatchHandlers<Integer> catchHandlers = CatchHandlers.EMPTY_INDICES; |
| 174 | |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 175 | // Linked list of instructions belonging to this block. |
| 176 | private final InstructionList instructions = new InstructionList(this); |
Ian Zerny | 9004d05 | 2023-03-02 20:15:36 +0100 | [diff] [blame] | 177 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 178 | private int number = -1; |
| 179 | private List<Phi> phis = new ArrayList<>(); |
| 180 | |
| 181 | // State used during SSA construction. The SSA construction is based on the paper: |
| 182 | // |
| 183 | // "Simple and Efficient Construction of Static Single Assignment Form" |
| 184 | // http://compilers.cs.uni-saarland.de/papers/bbhlmz13cc.pdf |
| 185 | // |
| 186 | // A basic block is filled when local value numbering is complete for that block. |
| 187 | // A basic block is sealed when all predecessor blocks have been filled. |
| 188 | // |
| 189 | // Therefore, for a sealed block we can always search backwards to find reaching values |
| 190 | // in predecessor blocks. |
| 191 | private boolean filled = false; |
| 192 | private boolean sealed = false; |
Sebastien Hertz | 39e884b | 2018-01-10 17:18:02 +0100 | [diff] [blame] | 193 | private final Map<Integer, Phi> incompletePhis = new HashMap<>(); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 194 | private int estimatedPredecessorsCount = 0; |
| 195 | private int unfilledPredecessorsCount = 0; |
| 196 | |
| 197 | // State used for basic block sorting and tracing. |
| 198 | private int color = 0; |
| 199 | |
| 200 | // Map of registers to current SSA value. Used during SSA numbering and cleared once filled. |
| 201 | private Map<Integer, Value> currentDefinitions = new HashMap<>(); |
| 202 | |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 203 | // Metadata shared by all blocks of an IRCode. |
| 204 | // TODO(b/376663044): Remove |metadata| parameter from methods in this class. |
| 205 | private IRMetadata metadata; |
| 206 | |
| 207 | public BasicBlock(IRMetadata metadata) { |
| 208 | this.metadata = metadata; |
| 209 | } |
| 210 | |
| 211 | public IRMetadata getMetadata() { |
| 212 | return metadata; |
| 213 | } |
| 214 | |
| 215 | // Required when moving a block between code objects (inlining). |
| 216 | void setMetadata(IRMetadata metadata) { |
| 217 | this.metadata = metadata; |
| 218 | } |
| 219 | |
Christoffer Quist Adamsen | e1fa9de | 2022-04-22 12:56:21 +0200 | [diff] [blame] | 220 | public <BT, CT> TraversalContinuation<BT, CT> traverseNormalPredecessors( |
| 221 | BiFunction<? super BasicBlock, ? super CT, TraversalContinuation<BT, CT>> fn, |
| 222 | CT initialValue) { |
| 223 | TraversalContinuation<BT, CT> traversalContinuation = |
| 224 | TraversalContinuation.doContinue(initialValue); |
| 225 | for (BasicBlock predecessor : getPredecessors()) { |
| 226 | if (predecessor.hasCatchSuccessor(this)) { |
| 227 | continue; |
| 228 | } |
| 229 | traversalContinuation = |
| 230 | fn.apply(predecessor, traversalContinuation.asContinue().getValueOrDefault(null)); |
| 231 | if (traversalContinuation.isBreak()) { |
| 232 | break; |
| 233 | } |
| 234 | } |
| 235 | return traversalContinuation; |
| 236 | } |
| 237 | |
| 238 | public <BT, CT> TraversalContinuation<BT, CT> traverseNormalSuccessors( |
| 239 | BiFunction<? super BasicBlock, ? super CT, TraversalContinuation<BT, CT>> fn, |
| 240 | CT initialValue) { |
| 241 | TraversalContinuation<BT, CT> traversalContinuation = |
| 242 | TraversalContinuation.doContinue(initialValue); |
| 243 | for (int i = successors.size() - numberOfNormalSuccessors(); i < successors.size(); i++) { |
| 244 | traversalContinuation = |
| 245 | fn.apply(successors.get(i), traversalContinuation.asContinue().getValueOrDefault(null)); |
| 246 | if (traversalContinuation.isBreak()) { |
| 247 | break; |
| 248 | } |
| 249 | } |
| 250 | return traversalContinuation; |
| 251 | } |
| 252 | |
| 253 | public <BT, CT> TraversalContinuation<BT, CT> traverseExceptionalPredecessors( |
| 254 | BiFunction<? super BasicBlock, ? super CT, TraversalContinuation<BT, CT>> fn, |
| 255 | CT initialValue) { |
| 256 | TraversalContinuation<BT, CT> traversalContinuation = |
| 257 | TraversalContinuation.doContinue(initialValue); |
| 258 | for (BasicBlock predecessor : getPredecessors()) { |
| 259 | if (!predecessor.hasCatchSuccessor(this)) { |
| 260 | continue; |
| 261 | } |
| 262 | traversalContinuation = |
| 263 | fn.apply(predecessor, traversalContinuation.asContinue().getValueOrDefault(null)); |
| 264 | if (traversalContinuation.isBreak()) { |
| 265 | break; |
| 266 | } |
| 267 | } |
| 268 | return traversalContinuation; |
| 269 | } |
| 270 | |
| 271 | public <BT, CT> TraversalContinuation<BT, CT> traverseExceptionalSuccessors( |
Christoffer Quist Adamsen | f927336 | 2022-06-16 15:14:06 +0200 | [diff] [blame] | 272 | TriFunction<? super BasicBlock, DexType, ? super CT, TraversalContinuation<BT, CT>> fn, |
Christoffer Quist Adamsen | e1fa9de | 2022-04-22 12:56:21 +0200 | [diff] [blame] | 273 | CT initialValue) { |
| 274 | int numberOfExceptionalSuccessors = numberOfExceptionalSuccessors(); |
| 275 | TraversalContinuation<BT, CT> traversalContinuation = |
| 276 | TraversalContinuation.doContinue(initialValue); |
| 277 | for (int i = 0; i < numberOfExceptionalSuccessors; i++) { |
| 278 | traversalContinuation = |
Christoffer Quist Adamsen | f927336 | 2022-06-16 15:14:06 +0200 | [diff] [blame] | 279 | fn.apply( |
| 280 | successors.get(i), |
| 281 | catchHandlers.getGuard(i), |
| 282 | traversalContinuation.asContinue().getValueOrDefault(null)); |
Christoffer Quist Adamsen | e1fa9de | 2022-04-22 12:56:21 +0200 | [diff] [blame] | 283 | if (traversalContinuation.isBreak()) { |
| 284 | break; |
| 285 | } |
| 286 | } |
| 287 | return traversalContinuation; |
| 288 | } |
| 289 | |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 290 | public void addControlFlowEdgesMayChangeListener(BasicBlockChangeListener listener) { |
| 291 | if (onControlFlowEdgesMayChangeListeners == null) { |
| 292 | // WeakSet to allow the listeners to be garbage collected. |
| 293 | onControlFlowEdgesMayChangeListeners = Collections.newSetFromMap(new WeakHashMap<>()); |
| 294 | } |
| 295 | onControlFlowEdgesMayChangeListeners.add(listener); |
| 296 | } |
| 297 | |
Christoffer Quist Adamsen | 0db0767 | 2020-04-02 08:40:06 +0200 | [diff] [blame] | 298 | public boolean hasUniqueSuccessor() { |
| 299 | return successors.size() == 1; |
| 300 | } |
| 301 | |
Christoffer Quist Adamsen | 575e3bf | 2021-02-26 11:09:55 +0100 | [diff] [blame] | 302 | public boolean hasUniqueSuccessorWithUniquePredecessor() { |
| 303 | return hasUniqueSuccessor() && getUniqueSuccessor().getPredecessors().size() == 1; |
| 304 | } |
| 305 | |
Christoffer Quist Adamsen | 305a54a | 2020-04-03 15:34:36 +0200 | [diff] [blame] | 306 | public boolean hasUniqueNormalSuccessor() { |
| 307 | return numberOfNormalSuccessors() == 1; |
| 308 | } |
| 309 | |
| 310 | public boolean hasUniqueNormalSuccessorWithUniquePredecessor() { |
| 311 | return hasUniqueNormalSuccessor() && getUniqueNormalSuccessor().getPredecessors().size() == 1; |
| 312 | } |
| 313 | |
Christoffer Quist Adamsen | 0db0767 | 2020-04-02 08:40:06 +0200 | [diff] [blame] | 314 | public BasicBlock getUniqueSuccessor() { |
Christoffer Quist Adamsen | 305a54a | 2020-04-03 15:34:36 +0200 | [diff] [blame] | 315 | assert hasUniqueSuccessor(); |
| 316 | return successors.get(0); |
| 317 | } |
| 318 | |
| 319 | public BasicBlock getUniqueNormalSuccessor() { |
| 320 | assert hasUniqueNormalSuccessor(); |
Christoffer Quist Adamsen | 18e2e11 | 2020-04-03 20:03:46 +0200 | [diff] [blame] | 321 | return ListUtils.last(successors); |
Christoffer Quist Adamsen | 0db0767 | 2020-04-02 08:40:06 +0200 | [diff] [blame] | 322 | } |
| 323 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 324 | public List<BasicBlock> getSuccessors() { |
Andrew Grieve | 17df7c5 | 2023-11-10 16:38:59 -0500 | [diff] [blame] | 325 | return ListUtils.unmodifiableForTesting(successors); |
Christoffer Quist Adamsen | 6c71cd9 | 2018-12-12 09:56:03 +0100 | [diff] [blame] | 326 | } |
| 327 | |
| 328 | public List<BasicBlock> getMutableSuccessors() { |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 329 | assert notifySuccessorsMayChangeListeners(); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 330 | return successors; |
| 331 | } |
| 332 | |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 333 | private boolean notifySuccessorsMayChangeListeners() { |
| 334 | if (onControlFlowEdgesMayChangeListeners != null) { |
| 335 | onControlFlowEdgesMayChangeListeners.forEach(l -> l.onSuccessorsMayChange(this)); |
| 336 | } |
| 337 | return true; |
| 338 | } |
| 339 | |
Christoffer Quist Adamsen | 8100cf2 | 2020-04-03 13:43:31 +0200 | [diff] [blame] | 340 | public void forEachNormalSuccessor(Consumer<BasicBlock> consumer) { |
Christoffer Quist Adamsen | 18e2e11 | 2020-04-03 20:03:46 +0200 | [diff] [blame] | 341 | for (int i = successors.size() - numberOfNormalSuccessors(); i < successors.size(); i++) { |
Christoffer Quist Adamsen | 8100cf2 | 2020-04-03 13:43:31 +0200 | [diff] [blame] | 342 | consumer.accept(successors.get(i)); |
| 343 | } |
| 344 | } |
| 345 | |
| 346 | public boolean hasNormalSuccessor(BasicBlock block) { |
Christoffer Quist Adamsen | 18e2e11 | 2020-04-03 20:03:46 +0200 | [diff] [blame] | 347 | for (int i = successors.size() - numberOfNormalSuccessors(); i < successors.size(); i++) { |
Christoffer Quist Adamsen | 8100cf2 | 2020-04-03 13:43:31 +0200 | [diff] [blame] | 348 | if (successors.get(i) == block) { |
| 349 | return true; |
| 350 | } |
| 351 | } |
| 352 | return false; |
| 353 | } |
| 354 | |
Christoffer Quist Adamsen | ab63a16 | 2023-09-06 11:16:57 +0200 | [diff] [blame] | 355 | @SuppressWarnings("MixedMutabilityReturnType") |
Ian Zerny | ad5d0a3 | 2017-10-06 11:11:20 +0200 | [diff] [blame] | 356 | public List<BasicBlock> getNormalSuccessors() { |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 357 | if (!hasCatchHandlers()) { |
| 358 | return successors; |
| 359 | } |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 360 | ImmutableList.Builder<BasicBlock> normals = ImmutableList.builder(); |
Christoffer Quist Adamsen | 8100cf2 | 2020-04-03 13:43:31 +0200 | [diff] [blame] | 361 | forEachNormalSuccessor(normals::add); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 362 | return normals.build(); |
| 363 | } |
| 364 | |
Christoffer Quist Adamsen | e7479ea | 2019-10-10 16:04:01 +0200 | [diff] [blame] | 365 | public int numberOfNormalSuccessors() { |
| 366 | if (hasCatchHandlers()) { |
| 367 | return successors.size() - catchHandlers.getUniqueTargets().size(); |
| 368 | } |
| 369 | return successors.size(); |
| 370 | } |
| 371 | |
Christoffer Quist Adamsen | e345445 | 2020-04-22 09:08:03 +0200 | [diff] [blame] | 372 | public int numberOfExceptionalSuccessors() { |
| 373 | if (hasCatchHandlers()) { |
| 374 | return catchHandlers.getUniqueTargets().size(); |
| 375 | } |
| 376 | return 0; |
| 377 | } |
| 378 | |
Christoffer Quist Adamsen | 6347ab5 | 2019-06-19 15:30:17 +0200 | [diff] [blame] | 379 | public boolean hasUniquePredecessor() { |
| 380 | return predecessors.size() == 1; |
| 381 | } |
| 382 | |
Andrew Grieve | 6a196b7 | 2025-01-28 09:45:13 -0500 | [diff] [blame] | 383 | public boolean hasUniquePredecessorWithUniqueSuccessor() { |
| 384 | return hasUniquePredecessor() && getUniquePredecessor().getSuccessors().size() == 1; |
| 385 | } |
| 386 | |
Christoffer Quist Adamsen | 6347ab5 | 2019-06-19 15:30:17 +0200 | [diff] [blame] | 387 | public BasicBlock getUniquePredecessor() { |
| 388 | assert hasUniquePredecessor(); |
| 389 | return predecessors.get(0); |
| 390 | } |
| 391 | |
Christoffer Adamsen | 31b8ed1 | 2024-08-29 16:59:58 +0200 | [diff] [blame] | 392 | public BasicBlock getPredecessor(int i) { |
| 393 | return getPredecessors().get(i); |
| 394 | } |
| 395 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 396 | public List<BasicBlock> getPredecessors() { |
Andrew Grieve | 17df7c5 | 2023-11-10 16:38:59 -0500 | [diff] [blame] | 397 | return ListUtils.unmodifiableForTesting(predecessors); |
Christoffer Quist Adamsen | 6c71cd9 | 2018-12-12 09:56:03 +0100 | [diff] [blame] | 398 | } |
| 399 | |
| 400 | public List<BasicBlock> getMutablePredecessors() { |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 401 | assert notifyPredecessorsMayChangeListeners(); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 402 | return predecessors; |
| 403 | } |
| 404 | |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 405 | private boolean notifyPredecessorsMayChangeListeners() { |
| 406 | if (onControlFlowEdgesMayChangeListeners != null) { |
| 407 | onControlFlowEdgesMayChangeListeners.forEach(l -> l.onPredecessorsMayChange(this)); |
| 408 | } |
| 409 | return true; |
| 410 | } |
| 411 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 412 | public List<BasicBlock> getNormalPredecessors() { |
| 413 | ImmutableList.Builder<BasicBlock> normals = ImmutableList.builder(); |
| 414 | for (BasicBlock predecessor : predecessors) { |
Mads Ager | c52c353 | 2017-06-14 11:54:02 +0200 | [diff] [blame] | 415 | if (!predecessor.hasCatchSuccessor(this)) { |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 416 | normals.add(predecessor); |
| 417 | } |
| 418 | } |
| 419 | return normals.build(); |
| 420 | } |
| 421 | |
| 422 | public void removeSuccessor(BasicBlock block) { |
| 423 | int index = successors.indexOf(block); |
| 424 | assert index >= 0 : "removeSuccessor did not find the successor to remove"; |
Christoffer Quist Adamsen | 95dd66b | 2018-06-11 09:12:45 +0200 | [diff] [blame] | 425 | removeSuccessorsByIndex(new IntArrayList(new int[] {index})); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 426 | } |
| 427 | |
Christoffer Quist Adamsen | 4111aba | 2024-01-23 15:35:55 +0100 | [diff] [blame] | 428 | public void removePredecessor(BasicBlock block) { |
| 429 | removePredecessor(block, null); |
| 430 | } |
| 431 | |
| 432 | public void removePredecessor(BasicBlock block, AffectedValues affectedValues) { |
| 433 | removePredecessor(block, affectedValues, emptyConsumer(), alwaysFalse()); |
| 434 | } |
| 435 | |
| 436 | public void removePredecessor( |
| 437 | BasicBlock block, |
| 438 | AffectedValues affectedValues, |
| 439 | Consumer<Value> prunedValueConsumer, |
| 440 | Predicate<BasicBlock> removedBlocks) { |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 441 | int index = predecessors.indexOf(block); |
| 442 | assert index >= 0 : "removePredecessor did not find the predecessor to remove"; |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 443 | getMutablePredecessors().remove(index); |
Christoffer Quist Adamsen | 4111aba | 2024-01-23 15:35:55 +0100 | [diff] [blame] | 444 | if (hasPhis()) { |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 445 | for (Phi phi : getPhis()) { |
Christoffer Quist Adamsen | 4111aba | 2024-01-23 15:35:55 +0100 | [diff] [blame] | 446 | phi.removeOperand(index, affectedValues, removedBlocks); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 447 | } |
| 448 | // Collect and remove trivial phis after block removal. |
| 449 | List<Phi> trivials = new ArrayList<>(); |
| 450 | for (Phi phi : getPhis()) { |
| 451 | if (phi.isTrivialPhi()) { |
| 452 | trivials.add(phi); |
| 453 | } |
| 454 | } |
| 455 | for (Phi phi : trivials) { |
Christoffer Quist Adamsen | 4111aba | 2024-01-23 15:35:55 +0100 | [diff] [blame] | 456 | phi.removeTrivialPhi(null, affectedValues, prunedValueConsumer, removedBlocks); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 457 | } |
| 458 | } |
| 459 | } |
| 460 | |
Christoffer Quist Adamsen | 078d674 | 2019-06-19 15:34:13 +0200 | [diff] [blame] | 461 | public void removeAllNormalSuccessors() { |
| 462 | if (hasCatchHandlers()) { |
| 463 | IntList successorsToRemove = new IntArrayList(); |
Andrew Grieve | 38e77b9 | 2024-01-19 13:53:08 -0500 | [diff] [blame] | 464 | |
| 465 | for (int i = numberOfExceptionalSuccessors(), l = successors.size(); i < l; i++) { |
| 466 | successorsToRemove.add(i); |
Christoffer Quist Adamsen | 078d674 | 2019-06-19 15:34:13 +0200 | [diff] [blame] | 467 | } |
| 468 | removeSuccessorsByIndex(successorsToRemove); |
| 469 | } else { |
| 470 | successors.clear(); |
| 471 | } |
| 472 | } |
| 473 | |
Andrew Grieve | 712a8ca | 2024-01-18 15:42:45 -0500 | [diff] [blame] | 474 | public void removeAllExceptionalSuccessors() { |
| 475 | assert hasCatchHandlers(); |
| 476 | IntList successorsToRemove = new IntArrayList(); |
| 477 | int numberOfExceptionalSuccessors = numberOfExceptionalSuccessors(); |
| 478 | for (int i = 0; i < numberOfExceptionalSuccessors; i++) { |
| 479 | successorsToRemove.add(i); |
| 480 | successors.get(i).getMutablePredecessors().remove(this); |
| 481 | } |
| 482 | removeSuccessorsByIndex(successorsToRemove); |
| 483 | } |
| 484 | |
Denis Vnukov | 4dc50a3 | 2018-02-09 07:43:50 -0800 | [diff] [blame] | 485 | public void swapSuccessors(BasicBlock a, BasicBlock b) { |
| 486 | assert a != b; |
| 487 | int aIndex = successors.indexOf(a); |
| 488 | int bIndex = successors.indexOf(b); |
| 489 | assert aIndex >= 0 && bIndex >= 0; |
| 490 | swapSuccessorsByIndex(aIndex, bIndex); |
| 491 | } |
| 492 | |
Denis Vnukov | d1fd13b | 2018-01-22 12:08:19 -0800 | [diff] [blame] | 493 | public void swapSuccessorsByIndex(int index1, int index2) { |
| 494 | assert index1 != index2; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 495 | if (hasCatchHandlers()) { |
| 496 | List<Integer> targets = new ArrayList<>(catchHandlers.getAllTargets()); |
Denis Vnukov | d1fd13b | 2018-01-22 12:08:19 -0800 | [diff] [blame] | 497 | assert targets.contains(index1) == targets.contains(index2) |
| 498 | : "Swapping normal successor and catch handler"; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 499 | for (int i = 0; i < targets.size(); i++) { |
Denis Vnukov | d1fd13b | 2018-01-22 12:08:19 -0800 | [diff] [blame] | 500 | if (targets.get(i) == index1) { |
| 501 | targets.set(i, index2); |
| 502 | } else if (targets.get(i) == index2) { |
| 503 | targets.set(i, index1); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 504 | } |
| 505 | } |
| 506 | catchHandlers = new CatchHandlers<>(catchHandlers.getGuards(), targets); |
| 507 | } |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 508 | List<BasicBlock> successors = getMutableSuccessors(); |
Denis Vnukov | d1fd13b | 2018-01-22 12:08:19 -0800 | [diff] [blame] | 509 | BasicBlock tmp = successors.get(index1); |
| 510 | successors.set(index1, successors.get(index2)); |
| 511 | successors.set(index2, tmp); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 512 | } |
| 513 | |
Ian Zerny | 7271f71 | 2018-09-20 09:22:59 +0200 | [diff] [blame] | 514 | // TODO(b/116174212): Remove the predecessor pointer from the old successor block. |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 515 | public void replaceSuccessor(BasicBlock block, BasicBlock newBlock) { |
| 516 | assert successors.contains(block) : "attempt to replace non-existent successor"; |
| 517 | |
| 518 | if (successors.contains(newBlock)) { |
| 519 | int indexOfOldBlock = successors.indexOf(block); |
| 520 | int indexOfNewBlock = successors.indexOf(newBlock); |
| 521 | |
| 522 | // Always rewrite catch handlers. |
| 523 | if (hasCatchHandlers()) { |
| 524 | List<Integer> targets = new ArrayList<>(catchHandlers.getAllTargets()); |
| 525 | for (int i = 0; i < targets.size(); i++) { |
| 526 | if (targets.get(i) == indexOfOldBlock) { |
| 527 | targets.set(i, indexOfNewBlock); |
| 528 | } |
| 529 | if (targets.get(i) > indexOfOldBlock) { |
| 530 | targets.set(i, targets.get(i) - 1); |
| 531 | } |
| 532 | } |
| 533 | catchHandlers = new CatchHandlers<>(catchHandlers.getGuards(), targets); |
| 534 | } |
| 535 | |
| 536 | // Check if the replacement influences jump targets and rewrite as needed. |
| 537 | if (exit().isGoto()) { |
| 538 | if (indexOfOldBlock == successors.size() - 1 && indexOfNewBlock != successors.size() - 2) { |
| 539 | // Replacing the goto target and the new block will not become the goto target. |
| 540 | // We perform a swap to get the new block into the goto target position. |
Denis Vnukov | d1fd13b | 2018-01-22 12:08:19 -0800 | [diff] [blame] | 541 | swapSuccessorsByIndex(indexOfOldBlock - 1, indexOfNewBlock); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 542 | } |
| 543 | } else if (exit().isIf()) { |
| 544 | if (indexOfNewBlock >= successors.size() - 2 && indexOfOldBlock >= successors.size() - 2) { |
| 545 | // New and old are true target and fallthrough, replace last instruction with a goto. |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 546 | Instruction instruction = instructions.getLast(); |
| 547 | instructions.removeIgnoreValues(instruction); |
Mathias Rav | 7a3ced2 | 2018-03-21 15:37:37 +0100 | [diff] [blame] | 548 | // Iterate in reverse order to ensure that POP instructions are inserted in correct order. |
| 549 | for (int i = instruction.inValues().size() - 1; i >= 0; i--) { |
| 550 | Value value = instruction.inValues().get(i); |
Morten Krogh-Jespersen | ff0905b | 2020-11-03 14:19:35 +0100 | [diff] [blame] | 551 | if (value.isValueOnStack()) { |
| 552 | if (!value.isPhi() |
| 553 | && (value.definition.isLoad() && value.definition.getBlock() == this)) { |
Morten Krogh-Jespersen | 532989d | 2018-10-30 14:28:03 +0100 | [diff] [blame] | 554 | assert hasLinearFlow(this, value.definition.getBlock()); |
| 555 | value.definition.getBlock().removeInstruction(value.definition); |
Mathias Rav | 7a3ced2 | 2018-03-21 15:37:37 +0100 | [diff] [blame] | 556 | } else { |
Morten Krogh-Jespersen | ff0905b | 2020-11-03 14:19:35 +0100 | [diff] [blame] | 557 | assert !(value instanceof StackValues); |
| 558 | Pop pop = new Pop(value); |
Mathias Rav | 7a3ced2 | 2018-03-21 15:37:37 +0100 | [diff] [blame] | 559 | pop.setPosition(instruction.getPosition()); |
| 560 | getInstructions().addLast(pop); |
| 561 | } |
| 562 | } |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 563 | if (value.hasUsersInfo()) { |
| 564 | value.removeUser(instruction); |
| 565 | } |
| 566 | } |
| 567 | Instruction exit = new Goto(); |
Ian Zerny | 0dba4dd | 2018-09-26 14:48:22 +0200 | [diff] [blame] | 568 | exit.setPosition(instruction.getPosition()); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 569 | getInstructions().addLast(exit); |
| 570 | } else if (indexOfOldBlock >= successors.size() - 2) { |
| 571 | // Old is either true or fallthrough and we need to swap the new block into the right |
| 572 | // position to become that target. |
Denis Vnukov | d1fd13b | 2018-01-22 12:08:19 -0800 | [diff] [blame] | 573 | swapSuccessorsByIndex(indexOfOldBlock - 1, indexOfNewBlock); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 574 | } |
Christoffer Quist Adamsen | c17fa82 | 2019-06-20 15:43:22 +0200 | [diff] [blame] | 575 | } else if (exit().isSwitch()) { |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 576 | // Rewrite fallthrough and case target indices. |
Christoffer Quist Adamsen | c17fa82 | 2019-06-20 15:43:22 +0200 | [diff] [blame] | 577 | Switch exit = exit().asSwitch(); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 578 | if (exit.getFallthroughBlockIndex() == indexOfOldBlock) { |
| 579 | exit.setFallthroughBlockIndex(indexOfNewBlock); |
| 580 | } |
| 581 | if (exit.getFallthroughBlockIndex() > indexOfOldBlock) { |
| 582 | exit.setFallthroughBlockIndex(exit.getFallthroughBlockIndex() - 1); |
| 583 | } |
| 584 | int[] indices = exit.targetBlockIndices(); |
| 585 | for (int i = 0; i < indices.length; i++) { |
| 586 | if (indices[i] == indexOfOldBlock) { |
| 587 | indices[i] = indexOfNewBlock; |
| 588 | } |
| 589 | if (indices[i] > indexOfOldBlock) { |
| 590 | indices[i] = indices[i] - 1; |
| 591 | } |
| 592 | } |
Christoffer Quist Adamsen | 078d674 | 2019-06-19 15:34:13 +0200 | [diff] [blame] | 593 | } else { |
| 594 | assert exit().isReturn() || exit().isThrow(); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 595 | } |
| 596 | |
| 597 | // Remove the replaced successor. |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 598 | boolean removed = getMutableSuccessors().remove(block); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 599 | assert removed; |
| 600 | } else { |
| 601 | // If the new block is not a successor we don't have to rewrite indices or instructions |
| 602 | // and we can just replace the old successor with the new one. |
| 603 | for (int i = 0; i < successors.size(); i++) { |
| 604 | if (successors.get(i) == block) { |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 605 | getMutableSuccessors().set(i, newBlock); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 606 | return; |
| 607 | } |
| 608 | } |
| 609 | } |
| 610 | } |
| 611 | |
Morten Krogh-Jespersen | 532989d | 2018-10-30 14:28:03 +0100 | [diff] [blame] | 612 | private boolean hasLinearFlow(BasicBlock current, BasicBlock target) { |
| 613 | while (current != target) { |
| 614 | if (current.getPredecessors().size() != 1) { |
| 615 | return false; |
| 616 | } |
| 617 | BasicBlock candidate = current.getPredecessors().get(0); |
| 618 | if (!candidate.exit().isGoto() || candidate.exit().asGoto().getTarget() != current) { |
| 619 | return false; |
| 620 | } |
| 621 | current = candidate; |
| 622 | } |
| 623 | return true; |
| 624 | } |
| 625 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 626 | public void replacePredecessor(BasicBlock block, BasicBlock newBlock) { |
| 627 | for (int i = 0; i < predecessors.size(); i++) { |
| 628 | if (predecessors.get(i) == block) { |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 629 | assert notifyPredecessorsMayChangeListeners(); |
| 630 | getMutablePredecessors().set(i, newBlock); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 631 | return; |
| 632 | } |
| 633 | } |
| 634 | assert false : "replaceSuccessor did not find the predecessor to replace"; |
| 635 | } |
| 636 | |
Christoffer Quist Adamsen | 95dd66b | 2018-06-11 09:12:45 +0200 | [diff] [blame] | 637 | public void removeSuccessorsByIndex(IntList successorsToRemove) { |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 638 | if (successorsToRemove.isEmpty()) { |
| 639 | return; |
| 640 | } |
Denis Vnukov | d1fd13b | 2018-01-22 12:08:19 -0800 | [diff] [blame] | 641 | assert ListUtils.verifyListIsOrdered(successorsToRemove); |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 642 | List<BasicBlock> successors = getMutableSuccessors(); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 643 | List<BasicBlock> copy = new ArrayList<>(successors); |
| 644 | successors.clear(); |
| 645 | int current = 0; |
| 646 | for (int i : successorsToRemove) { |
| 647 | successors.addAll(copy.subList(current, i)); |
| 648 | current = i + 1; |
| 649 | } |
| 650 | successors.addAll(copy.subList(current, copy.size())); |
| 651 | |
| 652 | if (hasCatchHandlers()) { |
Denis Vnukov | d1fd13b | 2018-01-22 12:08:19 -0800 | [diff] [blame] | 653 | List<Integer> currentTargets = catchHandlers.getAllTargets(); |
| 654 | List<DexType> currentGuards = catchHandlers.getGuards(); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 655 | int size = catchHandlers.size(); |
Denis Vnukov | d1fd13b | 2018-01-22 12:08:19 -0800 | [diff] [blame] | 656 | List<DexType> newGuards = new ArrayList<>(size); |
| 657 | List<Integer> newTargets = new ArrayList<>(size); |
| 658 | |
| 659 | // Since targets represent indices in the list of successors, we |
| 660 | // need to remove targets/indices included in successorsToRemove, |
| 661 | // and decrease the rest of targets/indices to reflect removed successors. |
| 662 | outer: |
| 663 | for (int i = 0; i < currentTargets.size(); i++) { |
| 664 | int index = currentTargets.get(i); |
| 665 | int decreaseBy = 0; |
| 666 | for (int removedIndex : successorsToRemove) { |
| 667 | if (index == removedIndex) { |
| 668 | continue outer; // target was removed |
| 669 | } |
| 670 | if (index < removedIndex) { |
| 671 | break; |
| 672 | } |
| 673 | decreaseBy++; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 674 | } |
Denis Vnukov | d1fd13b | 2018-01-22 12:08:19 -0800 | [diff] [blame] | 675 | newTargets.add(index - decreaseBy); |
| 676 | newGuards.add(currentGuards.get(i)); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 677 | } |
Denis Vnukov | d1fd13b | 2018-01-22 12:08:19 -0800 | [diff] [blame] | 678 | |
| 679 | if (newTargets.isEmpty()) { |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 680 | catchHandlers = CatchHandlers.EMPTY_INDICES; |
| 681 | } else { |
Denis Vnukov | d1fd13b | 2018-01-22 12:08:19 -0800 | [diff] [blame] | 682 | catchHandlers = new CatchHandlers<>(newGuards, newTargets); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 683 | } |
| 684 | } |
| 685 | } |
| 686 | |
| 687 | public void removePredecessorsByIndex(List<Integer> predecessorsToRemove) { |
| 688 | if (predecessorsToRemove.isEmpty()) { |
| 689 | return; |
| 690 | } |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 691 | List<BasicBlock> predecessors = getMutablePredecessors(); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 692 | List<BasicBlock> copy = new ArrayList<>(predecessors); |
| 693 | predecessors.clear(); |
| 694 | int current = 0; |
| 695 | for (int i : predecessorsToRemove) { |
| 696 | predecessors.addAll(copy.subList(current, i)); |
| 697 | current = i + 1; |
| 698 | } |
| 699 | predecessors.addAll(copy.subList(current, copy.size())); |
| 700 | } |
| 701 | |
| 702 | public void removePhisByIndex(List<Integer> predecessorsToRemove) { |
| 703 | for (Phi phi : phis) { |
| 704 | phi.removeOperandsByIndex(predecessorsToRemove); |
| 705 | } |
| 706 | } |
| 707 | |
Christoffer Adamsen | 207a9f5 | 2024-12-19 11:57:04 +0100 | [diff] [blame] | 708 | public void removeTrivialPhis(AffectedValues affectedValues) { |
| 709 | Iterator<Phi> iterator = phis.iterator(); |
| 710 | while (iterator.hasNext()) { |
| 711 | Phi phi = iterator.next(); |
| 712 | if (phi.isTrivialPhi()) { |
| 713 | iterator.remove(); |
| 714 | phi.removeTrivialPhi(null, affectedValues); |
| 715 | } |
| 716 | } |
| 717 | } |
| 718 | |
Christoffer Quist Adamsen | 179f700 | 2019-07-25 08:43:47 +0200 | [diff] [blame] | 719 | public boolean hasPhis() { |
Christoffer Quist Adamsen | 4111aba | 2024-01-23 15:35:55 +0100 | [diff] [blame] | 720 | return phis != null && !phis.isEmpty(); |
Christoffer Quist Adamsen | 179f700 | 2019-07-25 08:43:47 +0200 | [diff] [blame] | 721 | } |
| 722 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 723 | public List<Phi> getPhis() { |
| 724 | return phis; |
| 725 | } |
| 726 | |
Christoffer Quist Adamsen | 01e790d | 2020-03-25 12:51:28 +0100 | [diff] [blame] | 727 | public boolean isEntry() { |
| 728 | return getPredecessors().isEmpty(); |
| 729 | } |
| 730 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 731 | public boolean isFilled() { |
| 732 | return filled; |
| 733 | } |
| 734 | |
Christoffer Quist Adamsen | 85bcf9e | 2021-05-21 13:38:35 +0200 | [diff] [blame] | 735 | public void setFilled() { |
| 736 | filled = true; |
| 737 | } |
| 738 | |
Mads Ager | d67ce0a | 2017-12-22 11:37:01 +0100 | [diff] [blame] | 739 | public void setFilledForTesting() { |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 740 | filled = true; |
| 741 | } |
| 742 | |
| 743 | public boolean hasCatchHandlers() { |
| 744 | assert catchHandlers != null; |
| 745 | return !catchHandlers.isEmpty(); |
| 746 | } |
| 747 | |
| 748 | public int getNumber() { |
| 749 | assert number >= 0; |
| 750 | return number; |
| 751 | } |
| 752 | |
| 753 | public void setNumber(int number) { |
| 754 | assert number >= 0; |
| 755 | this.number = number; |
| 756 | } |
| 757 | |
Mathias Rav | e905850 | 2018-05-15 10:13:49 +0200 | [diff] [blame] | 758 | public String getNumberAsString() { |
| 759 | return number >= 0 ? "" + number : "<unknown>"; |
| 760 | } |
| 761 | |
Christoffer Adamsen | 383bcd8 | 2018-04-26 14:23:31 +0200 | [diff] [blame] | 762 | public int numberInstructions(int nextInstructionNumber) { |
| 763 | for (Instruction instruction : instructions) { |
| 764 | instruction.setNumber(nextInstructionNumber); |
Søren Gjesse | 36acb52 | 2019-11-27 12:27:36 +0100 | [diff] [blame] | 765 | nextInstructionNumber += INSTRUCTION_NUMBER_DELTA; |
Christoffer Adamsen | 383bcd8 | 2018-04-26 14:23:31 +0200 | [diff] [blame] | 766 | } |
| 767 | return nextInstructionNumber; |
| 768 | } |
| 769 | |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 770 | public InstructionList getInstructions() { |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 771 | return instructions; |
| 772 | } |
| 773 | |
Christoffer Quist Adamsen | c406cdd | 2022-07-01 13:17:44 +0200 | [diff] [blame] | 774 | public <T extends Instruction> Iterable<T> getInstructions(Predicate<Instruction> predicate) { |
| 775 | return IterableUtils.filter(getInstructions(), predicate); |
| 776 | } |
| 777 | |
Christoffer Quist Adamsen | 4929264 | 2019-08-01 15:52:06 +0200 | [diff] [blame] | 778 | public Iterable<Instruction> instructionsAfter(Instruction instruction) { |
| 779 | return () -> iterator(instruction); |
| 780 | } |
| 781 | |
Christoffer Quist Adamsen | 684e281 | 2019-10-07 13:49:05 +0200 | [diff] [blame] | 782 | public Iterable<Instruction> instructionsBefore(Instruction instruction) { |
| 783 | return () -> |
| 784 | new Iterator<Instruction>() { |
| 785 | |
| 786 | private InstructionIterator iterator = iterator(); |
| 787 | private Instruction next = advance(); |
| 788 | |
| 789 | private Instruction advance() { |
| 790 | if (iterator.hasNext()) { |
| 791 | Instruction next = iterator.next(); |
| 792 | if (next != instruction) { |
| 793 | return next; |
| 794 | } |
| 795 | } |
| 796 | return null; |
| 797 | } |
| 798 | |
| 799 | @Override |
| 800 | public boolean hasNext() { |
| 801 | return next != null; |
| 802 | } |
| 803 | |
| 804 | @Override |
| 805 | public Instruction next() { |
| 806 | Instruction result = next; |
| 807 | if (result == null) { |
| 808 | throw new NoSuchElementException(); |
| 809 | } |
| 810 | next = advance(); |
| 811 | return result; |
| 812 | } |
| 813 | }; |
| 814 | } |
| 815 | |
Jinseong Jeon | c30c9e2 | 2018-02-06 10:36:19 -0800 | [diff] [blame] | 816 | public boolean isEmpty() { |
| 817 | return instructions.isEmpty(); |
| 818 | } |
| 819 | |
Christoffer Quist Adamsen | 33d22a2 | 2020-02-13 11:11:52 +0100 | [diff] [blame] | 820 | public int size() { |
| 821 | return instructions.size(); |
| 822 | } |
| 823 | |
Christoffer Quist Adamsen | e0f8fa6 | 2020-01-23 09:39:06 +0100 | [diff] [blame] | 824 | public boolean isReturnBlock() { |
| 825 | return exit().isReturn(); |
| 826 | } |
| 827 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 828 | public Instruction entry() { |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 829 | return instructions.getFirst(); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 830 | } |
| 831 | |
| 832 | public JumpInstruction exit() { |
| 833 | assert filled; |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 834 | assert instructions.getLast().isJumpInstruction(); |
| 835 | return instructions.getLast().asJumpInstruction(); |
| 836 | } |
| 837 | |
| 838 | public Instruction getLastInstruction() { |
| 839 | return instructions.getLast(); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 840 | } |
| 841 | |
Ian Zerny | 603d50d | 2017-08-02 12:38:23 +0200 | [diff] [blame] | 842 | public Instruction exceptionalExit() { |
| 843 | assert hasCatchHandlers(); |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 844 | for (Instruction ins = getLastInstruction(); ins != null; ins = ins.getPrev()) { |
| 845 | if (ins.instructionTypeCanThrow()) { |
| 846 | return ins; |
Ian Zerny | 603d50d | 2017-08-02 12:38:23 +0200 | [diff] [blame] | 847 | } |
| 848 | } |
Søren Gjesse | 1848f40 | 2018-07-11 12:57:01 +0200 | [diff] [blame] | 849 | return null; |
Ian Zerny | 603d50d | 2017-08-02 12:38:23 +0200 | [diff] [blame] | 850 | } |
| 851 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 852 | public void clearUserInfo() { |
| 853 | phis = null; |
| 854 | instructions.forEach(Instruction::clearUserInfo); |
| 855 | } |
| 856 | |
| 857 | public void buildDex(DexBuilder builder) { |
| 858 | for (Instruction instruction : instructions) { |
| 859 | instruction.buildDex(builder); |
| 860 | } |
| 861 | } |
| 862 | |
Søren Gjesse | 6a0a655 | 2018-01-19 13:35:35 +0100 | [diff] [blame] | 863 | public void mark(int color) { |
| 864 | assert color != 0; |
| 865 | assert !isMarked(color); |
| 866 | this.color |= color; |
| 867 | assert isMarked(color); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 868 | } |
| 869 | |
Søren Gjesse | 6a0a655 | 2018-01-19 13:35:35 +0100 | [diff] [blame] | 870 | public void clearMark(int color) { |
| 871 | assert color != 0; |
| 872 | this.color &= ~color; |
| 873 | assert !isMarked(color); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 874 | } |
| 875 | |
Søren Gjesse | 6a0a655 | 2018-01-19 13:35:35 +0100 | [diff] [blame] | 876 | public boolean isMarked(int color) { |
| 877 | assert color != 0; |
| 878 | return (this.color & color) != 0; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 879 | } |
| 880 | |
| 881 | public void incrementUnfilledPredecessorCount() { |
| 882 | ++unfilledPredecessorsCount; |
| 883 | ++estimatedPredecessorsCount; |
| 884 | } |
| 885 | |
| 886 | public void decrementUnfilledPredecessorCount(int n) { |
| 887 | unfilledPredecessorsCount -= n; |
| 888 | estimatedPredecessorsCount -= n; |
| 889 | } |
| 890 | |
| 891 | public void decrementUnfilledPredecessorCount() { |
| 892 | --unfilledPredecessorsCount; |
| 893 | --estimatedPredecessorsCount; |
| 894 | } |
| 895 | |
| 896 | public boolean verifyFilledPredecessors() { |
| 897 | assert estimatedPredecessorsCount == predecessors.size(); |
| 898 | assert unfilledPredecessorsCount == 0; |
| 899 | return true; |
| 900 | } |
| 901 | |
| 902 | public void addPhi(Phi phi) { |
| 903 | phis.add(phi); |
| 904 | } |
| 905 | |
| 906 | public void removePhi(Phi phi) { |
Christoffer Quist Adamsen | 4111aba | 2024-01-23 15:35:55 +0100 | [diff] [blame] | 907 | removePhi(phi, null, emptyConsumer()); |
| 908 | } |
| 909 | |
| 910 | public void removePhi( |
| 911 | Phi phi, AffectedValues affectedValues, Consumer<Value> prunedValueConsumer) { |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 912 | phis.remove(phi); |
Mathias Rav | 1298d07 | 2018-04-16 16:08:05 +0200 | [diff] [blame] | 913 | assert currentDefinitions == null || !currentDefinitions.containsValue(phi) |
| 914 | : "Attempt to remove Phi " + phi + " which is present in currentDefinitions"; |
Christoffer Quist Adamsen | 4111aba | 2024-01-23 15:35:55 +0100 | [diff] [blame] | 915 | if (affectedValues != null) { |
| 916 | affectedValues.remove(phi); |
| 917 | } |
| 918 | prunedValueConsumer.accept(phi); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 919 | } |
| 920 | |
Christoffer Quist Adamsen | 822ac82 | 2023-08-21 09:45:51 +0200 | [diff] [blame] | 921 | public void removePhis(Collection<Phi> phisToRemove) { |
| 922 | assert currentDefinitions == null || currentDefinitions.isEmpty(); |
| 923 | phis.removeAll(phisToRemove); |
| 924 | } |
| 925 | |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 926 | public void add(Instruction next, IRMetadata unused_metadata) { |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 927 | assert !isFilled(); |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 928 | instructions.addLast(next); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 929 | } |
| 930 | |
| 931 | public void close(IRBuilder builder) { |
| 932 | assert !isFilled(); |
| 933 | assert !instructions.isEmpty(); |
| 934 | filled = true; |
| 935 | sealed = unfilledPredecessorsCount == 0; |
| 936 | assert exit().isJumpInstruction(); |
| 937 | assert verifyNoValuesAfterThrowingInstruction(); |
| 938 | for (BasicBlock successor : successors) { |
| 939 | successor.filledPredecessor(builder); |
| 940 | } |
| 941 | } |
| 942 | |
| 943 | public void link(BasicBlock successor) { |
| 944 | assert !successors.contains(successor); |
| 945 | assert !successor.predecessors.contains(this); |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 946 | getMutableSuccessors().add(successor); |
| 947 | successor.getMutablePredecessors().add(this); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 948 | } |
| 949 | |
Christoffer Quist Adamsen | 4111aba | 2024-01-23 15:35:55 +0100 | [diff] [blame] | 950 | private static boolean blocksClean(Collection<BasicBlock> blocks) { |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 951 | blocks.forEach( |
| 952 | b -> { |
Christoffer Quist Adamsen | 4111aba | 2024-01-23 15:35:55 +0100 | [diff] [blame] | 953 | assert b.predecessors.isEmpty(); |
| 954 | assert b.successors.isEmpty(); |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 955 | }); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 956 | return true; |
| 957 | } |
| 958 | |
| 959 | /** |
| 960 | * Unlinks this block from a single predecessor. |
| 961 | * |
| 962 | * @return returns the unlinked predecessor. |
| 963 | */ |
| 964 | public BasicBlock unlinkSinglePredecessor() { |
| 965 | assert predecessors.size() == 1; |
| 966 | assert predecessors.get(0).successors.size() == 1; |
| 967 | BasicBlock unlinkedBlock = predecessors.get(0); |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 968 | unlinkedBlock.getMutableSuccessors().clear(); |
| 969 | getMutablePredecessors().clear(); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 970 | return unlinkedBlock; |
| 971 | } |
| 972 | |
Tamas Kenez | 82aac82 | 2018-04-24 12:01:01 +0200 | [diff] [blame] | 973 | /** Like unlinkSinglePredecessor but the predecessor may have multiple successors. */ |
| 974 | public void unlinkSinglePredecessorSiblingsAllowed() { |
| 975 | assert predecessors.size() == 1; // There are no critical edges. |
| 976 | assert predecessors.get(0).successors.contains(this); |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 977 | List<BasicBlock> predecessors = getMutablePredecessors(); |
| 978 | predecessors.get(0).getMutableSuccessors().remove(this); |
| 979 | getMutablePredecessors().clear(); |
Tamas Kenez | 82aac82 | 2018-04-24 12:01:01 +0200 | [diff] [blame] | 980 | } |
| 981 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 982 | /** |
| 983 | * Unlinks this block from a single normal successor. |
| 984 | * |
| 985 | * @return Returns the unlinked successor. |
| 986 | */ |
| 987 | public BasicBlock unlinkSingleSuccessor() { |
| 988 | assert !hasCatchHandlers(); |
| 989 | assert successors.size() == 1; |
| 990 | assert successors.get(0).predecessors.size() == 1; |
| 991 | BasicBlock unlinkedBlock = successors.get(0); |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 992 | unlinkedBlock.getMutablePredecessors().clear(); |
| 993 | getMutableSuccessors().clear(); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 994 | return unlinkedBlock; |
| 995 | } |
| 996 | |
| 997 | /** |
Mads Ager | 4f1ae51 | 2018-04-05 11:16:59 +0200 | [diff] [blame] | 998 | * Unlinks the current block based on the assumption that it is a catch handler. |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 999 | * |
Mads Ager | 4f1ae51 | 2018-04-05 11:16:59 +0200 | [diff] [blame] | 1000 | * Catch handlers always have only one predecessor and at most one successor. |
| 1001 | * That is because we have edge-split form for all exceptional flow. |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1002 | */ |
Mads Ager | 4f1ae51 | 2018-04-05 11:16:59 +0200 | [diff] [blame] | 1003 | public void unlinkCatchHandler() { |
| 1004 | assert predecessors.size() == 1; |
| 1005 | predecessors.get(0).removeSuccessor(this); |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 1006 | getMutablePredecessors().clear(); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1007 | } |
| 1008 | |
Christoffer Quist Adamsen | fd7c4d5 | 2018-11-05 17:51:55 +0100 | [diff] [blame] | 1009 | /** |
| 1010 | * Removes this basic block as a catch handler for the given guard, based on the assumption that |
| 1011 | * this block is a catch handler. |
| 1012 | * |
| 1013 | * <p>If this basic block is the target for a unique guard, then this catch handler will be |
| 1014 | * unlinked. If this basic block is the target for multiple guards, then the catch handlers of the |
| 1015 | * predecessor block will be updated such that this block is no longer a catch handler for the |
| 1016 | * given guard. |
| 1017 | */ |
| 1018 | public void unlinkCatchHandlerForGuard(DexType guard) { |
| 1019 | assert predecessors.size() == 1; |
| 1020 | if (isCatchHandlerForSingleGuard()) { |
| 1021 | // Unlink catch handler entirely. |
| 1022 | unlinkCatchHandler(); |
| 1023 | } else { |
| 1024 | // Update catch handlers of predecessor. |
| 1025 | BasicBlock predecessor = predecessors.get(0); |
| 1026 | predecessor.removeCatchHandlerWithGuard(guard); |
| 1027 | } |
| 1028 | } |
| 1029 | |
| 1030 | private void removeCatchHandlerWithGuard(DexType guard) { |
Christoffer Quist Adamsen | 7411e73 | 2018-11-06 13:14:29 +0100 | [diff] [blame] | 1031 | int guardIndex = catchHandlers.getGuards().indexOf(guard); |
| 1032 | if (guardIndex >= 0) { |
| 1033 | int successorIndex = catchHandlers.getAllTargets().get(guardIndex); |
| 1034 | assert successorIndex >= 0; |
Christoffer Quist Adamsen | fd7c4d5 | 2018-11-05 17:51:55 +0100 | [diff] [blame] | 1035 | catchHandlers = catchHandlers.removeGuard(guard); |
| 1036 | if (getCatchHandlers().getAllTargets().stream() |
| 1037 | .noneMatch(target -> target == successors.get(successorIndex))) { |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 1038 | getMutableSuccessors().remove(successorIndex); |
Christoffer Quist Adamsen | fd7c4d5 | 2018-11-05 17:51:55 +0100 | [diff] [blame] | 1039 | } |
| 1040 | assert consistentCatchHandlers(); |
| 1041 | } |
| 1042 | } |
| 1043 | |
| 1044 | private boolean isCatchHandlerForSingleGuard() { |
| 1045 | assert predecessors.size() == 1; |
| 1046 | BasicBlock predecessor = predecessors.get(0); |
| 1047 | assert predecessor.getCatchHandlers().getAllTargets().contains(this); |
| 1048 | int count = 0; |
| 1049 | for (BasicBlock target : predecessor.getCatchHandlers().getAllTargets()) { |
| 1050 | if (target == this && ++count > 1) { |
| 1051 | return false; |
| 1052 | } |
| 1053 | } |
| 1054 | return true; |
| 1055 | } |
| 1056 | |
Søren Gjesse | 3cd05ab | 2017-08-22 10:26:39 +0200 | [diff] [blame] | 1057 | public void detachAllSuccessors() { |
| 1058 | for (BasicBlock successor : successors) { |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 1059 | successor.getMutablePredecessors().remove(this); |
Søren Gjesse | 3cd05ab | 2017-08-22 10:26:39 +0200 | [diff] [blame] | 1060 | } |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 1061 | getMutableSuccessors().clear(); |
Søren Gjesse | 3cd05ab | 2017-08-22 10:26:39 +0200 | [diff] [blame] | 1062 | } |
| 1063 | |
Christoffer Quist Adamsen | 4111aba | 2024-01-23 15:35:55 +0100 | [diff] [blame] | 1064 | public Set<BasicBlock> unlink( |
| 1065 | BasicBlock successor, DominatorTree dominator, AffectedValues affectedValues) { |
Morten Krogh-Jespersen | 969699f | 2019-07-09 12:14:59 +0200 | [diff] [blame] | 1066 | assert affectedValues != null; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1067 | assert successors.contains(successor); |
Tamas Kenez | 82aac82 | 2018-04-24 12:01:01 +0200 | [diff] [blame] | 1068 | assert successor.predecessors.size() == 1; // There are no critical edges. |
| 1069 | assert successor.predecessors.get(0) == this; |
Christoffer Quist Adamsen | 4111aba | 2024-01-23 15:35:55 +0100 | [diff] [blame] | 1070 | Set<BasicBlock> dominatedBlocks = |
| 1071 | dominator.dominatedBlocks(successor, Sets.newIdentityHashSet()); |
| 1072 | for (BasicBlock dominatedBlock : dominatedBlocks) { |
| 1073 | dominatedBlock.cleanForRemoval(affectedValues, emptyConsumer(), dominatedBlocks::contains); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1074 | } |
Christoffer Quist Adamsen | 4111aba | 2024-01-23 15:35:55 +0100 | [diff] [blame] | 1075 | assert blocksClean(dominatedBlocks); |
| 1076 | return dominatedBlocks; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1077 | } |
| 1078 | |
Christoffer Quist Adamsen | 4111aba | 2024-01-23 15:35:55 +0100 | [diff] [blame] | 1079 | public void cleanForRemoval( |
| 1080 | AffectedValues affectedValues, |
| 1081 | Consumer<Value> prunedValueConsumer, |
| 1082 | Predicate<BasicBlock> removedBlocks) { |
Mads Ager | 4f1ae51 | 2018-04-05 11:16:59 +0200 | [diff] [blame] | 1083 | for (BasicBlock block : successors) { |
Christoffer Quist Adamsen | 4111aba | 2024-01-23 15:35:55 +0100 | [diff] [blame] | 1084 | block.removePredecessor(this, affectedValues, prunedValueConsumer, removedBlocks); |
Mads Ager | 4f1ae51 | 2018-04-05 11:16:59 +0200 | [diff] [blame] | 1085 | } |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 1086 | getMutableSuccessors().clear(); |
Mads Ager | 4f1ae51 | 2018-04-05 11:16:59 +0200 | [diff] [blame] | 1087 | for (BasicBlock block : predecessors) { |
| 1088 | block.removeSuccessor(this); |
| 1089 | } |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 1090 | getMutablePredecessors().clear(); |
Mads Ager | 4f1ae51 | 2018-04-05 11:16:59 +0200 | [diff] [blame] | 1091 | for (Phi phi : getPhis()) { |
Christoffer Quist Adamsen | 4111aba | 2024-01-23 15:35:55 +0100 | [diff] [blame] | 1092 | affectedValues.addLiveAffectedValuesOf(phi, removedBlocks); |
Mads Ager | 4f1ae51 | 2018-04-05 11:16:59 +0200 | [diff] [blame] | 1093 | for (Value operand : phi.getOperands()) { |
| 1094 | operand.removePhiUser(phi); |
| 1095 | } |
Christoffer Quist Adamsen | 4111aba | 2024-01-23 15:35:55 +0100 | [diff] [blame] | 1096 | affectedValues.remove(phi); |
Christoffer Quist Adamsen | b55c98e | 2024-01-22 15:26:00 +0100 | [diff] [blame] | 1097 | prunedValueConsumer.accept(phi); |
Mads Ager | 4f1ae51 | 2018-04-05 11:16:59 +0200 | [diff] [blame] | 1098 | } |
| 1099 | getPhis().clear(); |
| 1100 | for (Instruction instruction : getInstructions()) { |
Christoffer Quist Adamsen | b55c98e | 2024-01-22 15:26:00 +0100 | [diff] [blame] | 1101 | if (instruction.hasOutValue()) { |
Christoffer Quist Adamsen | 4111aba | 2024-01-23 15:35:55 +0100 | [diff] [blame] | 1102 | Value outValue = instruction.outValue(); |
| 1103 | affectedValues.addLiveAffectedValuesOf(outValue, removedBlocks); |
| 1104 | outValue.clearUsers(); |
| 1105 | instruction.setOutValue(null); |
| 1106 | affectedValues.remove(outValue); |
| 1107 | prunedValueConsumer.accept(outValue); |
Mads Ager | 4f1ae51 | 2018-04-05 11:16:59 +0200 | [diff] [blame] | 1108 | } |
Christoffer Quist Adamsen | 4111aba | 2024-01-23 15:35:55 +0100 | [diff] [blame] | 1109 | for (Value value : instruction.inValues()) { |
Mads Ager | 4f1ae51 | 2018-04-05 11:16:59 +0200 | [diff] [blame] | 1110 | value.removeUser(instruction); |
| 1111 | } |
| 1112 | for (Value value : instruction.getDebugValues()) { |
| 1113 | value.removeDebugUser(instruction); |
| 1114 | } |
| 1115 | } |
| 1116 | } |
| 1117 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1118 | public void linkCatchSuccessors(List<DexType> guards, List<BasicBlock> targets) { |
| 1119 | List<Integer> successorIndexes = new ArrayList<>(targets.size()); |
| 1120 | for (BasicBlock target : targets) { |
| 1121 | int index = successors.indexOf(target); |
| 1122 | if (index < 0) { |
| 1123 | index = successors.size(); |
| 1124 | link(target); |
| 1125 | } |
| 1126 | successorIndexes.add(index); |
| 1127 | } |
| 1128 | catchHandlers = new CatchHandlers<>(guards, successorIndexes); |
| 1129 | } |
| 1130 | |
Christoffer Quist Adamsen | e7479ea | 2019-10-10 16:04:01 +0200 | [diff] [blame] | 1131 | public void appendCatchHandler(BasicBlock target, DexType guard) { |
| 1132 | if (!canThrow()) { |
| 1133 | // Nothing to catch. |
| 1134 | return; |
| 1135 | } |
| 1136 | if (hasCatchHandlers()) { |
| 1137 | if (catchHandlers.getGuards().contains(guard)) { |
| 1138 | // Subsumed by an existing catch handler. |
| 1139 | return; |
| 1140 | } |
| 1141 | int targetIndex = successors.indexOf(target); |
| 1142 | if (targetIndex < 0) { |
| 1143 | List<BasicBlock> successors = getMutableSuccessors(); |
| 1144 | int numberOfSuccessors = successors.size(); |
| 1145 | int numberOfNormalSuccessors = numberOfNormalSuccessors(); |
| 1146 | if (numberOfNormalSuccessors > 0) { |
| 1147 | // Increase the size of the successor list by 1, and increase the index of each normal |
| 1148 | // successor by 1. |
Christoffer Quist Adamsen | d409075 | 2019-10-10 16:45:26 +0200 | [diff] [blame] | 1149 | targetIndex = numberOfSuccessors - numberOfNormalSuccessors; |
| 1150 | successors.add(targetIndex, target); |
Christoffer Quist Adamsen | e7479ea | 2019-10-10 16:04:01 +0200 | [diff] [blame] | 1151 | } else { |
| 1152 | // If there are no normal successors we can simply add the new catch handler. |
| 1153 | targetIndex = successors.size(); |
| 1154 | successors.add(target); |
| 1155 | } |
| 1156 | target.getMutablePredecessors().add(this); |
| 1157 | } |
| 1158 | catchHandlers = catchHandlers.appendGuard(guard, targetIndex); |
| 1159 | } else { |
| 1160 | assert instructions.stream().filter(Instruction::instructionTypeCanThrow).count() == 1; |
| 1161 | getMutableSuccessors().add(0, target); |
| 1162 | target.getMutablePredecessors().add(this); |
| 1163 | catchHandlers = new CatchHandlers<>(ImmutableList.of(guard), ImmutableList.of(0)); |
| 1164 | } |
Mads Ager | b2a1894 | 2018-08-23 13:37:02 +0200 | [diff] [blame] | 1165 | } |
| 1166 | |
Christoffer Quist Adamsen | 29441a1 | 2018-11-05 15:29:33 +0100 | [diff] [blame] | 1167 | /** |
| 1168 | * Due to class merging, it is possible that two exception classes have been merged into one. This |
Ian Zerny | d37ed28 | 2020-08-06 13:21:04 +0200 | [diff] [blame] | 1169 | * function renames the guards according to the given graph lens. |
Christoffer Quist Adamsen | 29441a1 | 2018-11-05 15:29:33 +0100 | [diff] [blame] | 1170 | * |
| 1171 | * @return true if any guards were renamed. |
| 1172 | */ |
Christoffer Quist Adamsen | d139da0 | 2023-09-05 19:05:13 +0200 | [diff] [blame] | 1173 | @SuppressWarnings("ReferenceEquality") |
Christoffer Quist Adamsen | 7ede342 | 2022-01-21 14:47:03 +0100 | [diff] [blame] | 1174 | public boolean renameGuardsInCatchHandlers(NonIdentityGraphLens graphLens, GraphLens codeLens) { |
Christoffer Quist Adamsen | 95dd66b | 2018-06-11 09:12:45 +0200 | [diff] [blame] | 1175 | assert hasCatchHandlers(); |
Christoffer Quist Adamsen | 29441a1 | 2018-11-05 15:29:33 +0100 | [diff] [blame] | 1176 | boolean anyGuardsRenamed = false; |
Christoffer Quist Adamsen | 95dd66b | 2018-06-11 09:12:45 +0200 | [diff] [blame] | 1177 | List<DexType> newGuards = new ArrayList<>(catchHandlers.getGuards().size()); |
| 1178 | for (DexType guard : catchHandlers.getGuards()) { |
| 1179 | // The type may have changed due to class merging. |
Christoffer Quist Adamsen | 7ede342 | 2022-01-21 14:47:03 +0100 | [diff] [blame] | 1180 | DexType renamed = graphLens.lookupType(guard, codeLens); |
Christoffer Quist Adamsen | 29441a1 | 2018-11-05 15:29:33 +0100 | [diff] [blame] | 1181 | newGuards.add(renamed); |
| 1182 | anyGuardsRenamed |= renamed != guard; |
Christoffer Quist Adamsen | 95dd66b | 2018-06-11 09:12:45 +0200 | [diff] [blame] | 1183 | } |
Christoffer Quist Adamsen | 29441a1 | 2018-11-05 15:29:33 +0100 | [diff] [blame] | 1184 | if (anyGuardsRenamed) { |
| 1185 | this.catchHandlers = new CatchHandlers<>(newGuards, catchHandlers.getAllTargets()); |
| 1186 | } |
| 1187 | return anyGuardsRenamed; |
Christoffer Quist Adamsen | 95dd66b | 2018-06-11 09:12:45 +0200 | [diff] [blame] | 1188 | } |
| 1189 | |
| 1190 | public boolean consistentCatchHandlers() { |
| 1191 | // Check that catch handlers are always the first successors of a block. |
| 1192 | if (hasCatchHandlers()) { |
Christoffer Adamsen | 0c68ba1 | 2024-11-22 14:21:30 +0100 | [diff] [blame] | 1193 | assert exit().isGoto() || exit().isReturn() || exit().isThrow(); |
Christoffer Quist Adamsen | 95dd66b | 2018-06-11 09:12:45 +0200 | [diff] [blame] | 1194 | CatchHandlers<Integer> catchHandlers = getCatchHandlersWithSuccessorIndexes(); |
Søren Gjesse | 315c053 | 2018-10-01 15:36:11 +0200 | [diff] [blame] | 1195 | // Check that guards are unique. |
| 1196 | assert catchHandlers.getGuards().size() |
| 1197 | == ImmutableSet.copyOf(catchHandlers.getGuards()).size(); |
Christoffer Quist Adamsen | 95dd66b | 2018-06-11 09:12:45 +0200 | [diff] [blame] | 1198 | // If there is a catch-all guard it must be the last. |
| 1199 | List<DexType> guards = catchHandlers.getGuards(); |
| 1200 | int lastGuardIndex = guards.size() - 1; |
| 1201 | for (int i = 0; i < guards.size(); i++) { |
Ian Zerny | c818a92 | 2019-03-04 10:38:55 +0100 | [diff] [blame] | 1202 | assert !guards.get(i).toDescriptorString().equals(DexItemFactory.throwableDescriptorString) |
| 1203 | || i == lastGuardIndex; |
Christoffer Quist Adamsen | 95dd66b | 2018-06-11 09:12:45 +0200 | [diff] [blame] | 1204 | } |
| 1205 | // Check that all successors except maybe the last are catch successors. |
| 1206 | List<Integer> sortedHandlerIndices = new ArrayList<>(catchHandlers.getAllTargets()); |
| 1207 | sortedHandlerIndices.sort(Comparator.naturalOrder()); |
| 1208 | int firstIndex = sortedHandlerIndices.get(0); |
| 1209 | int lastIndex = sortedHandlerIndices.get(sortedHandlerIndices.size() - 1); |
| 1210 | assert firstIndex == 0; |
| 1211 | assert lastIndex < sortedHandlerIndices.size(); |
| 1212 | int lastSuccessorIndex = getSuccessors().size() - 1; |
| 1213 | assert lastIndex == lastSuccessorIndex // All successors are catch successors. |
| 1214 | || lastIndex == lastSuccessorIndex - 1; // All but one successors are catch successors. |
| 1215 | assert lastIndex == lastSuccessorIndex || !exit().isThrow(); |
| 1216 | } |
| 1217 | return true; |
| 1218 | } |
| 1219 | |
Andrew Grieve | 24f16ea | 2024-01-23 09:36:03 -0500 | [diff] [blame] | 1220 | private BasicBlock getExceptionTrampolineTarget() { |
| 1221 | if (instructions.size() == 2 && entry().isMoveException() && exit().isGoto()) { |
| 1222 | assert !hasCatchHandlers() : "Trampoline should not have catch handlers"; |
| 1223 | return getUniqueNormalSuccessor(); |
| 1224 | } |
| 1225 | return null; |
Andrew Grieve | 712a8ca | 2024-01-18 15:42:45 -0500 | [diff] [blame] | 1226 | } |
| 1227 | |
Andrew Grieve | 24f16ea | 2024-01-23 09:36:03 -0500 | [diff] [blame] | 1228 | /** |
| 1229 | * Returns whether the given blocks are in the same try block. |
| 1230 | * |
| 1231 | * <p>They are considered the same if all catch handlers have the same guards and are trampolines |
| 1232 | * that point to the same block. |
| 1233 | */ |
Andrew Grieve | 712a8ca | 2024-01-18 15:42:45 -0500 | [diff] [blame] | 1234 | public boolean hasEquivalentCatchHandlers(BasicBlock other) { |
| 1235 | if (this == other) { |
| 1236 | return true; |
| 1237 | } |
| 1238 | List<Integer> targets1 = catchHandlers.getAllTargets(); |
| 1239 | List<Integer> targets2 = other.catchHandlers.getAllTargets(); |
Andrew Grieve | 24f16ea | 2024-01-23 09:36:03 -0500 | [diff] [blame] | 1240 | |
Andrew Grieve | 712a8ca | 2024-01-18 15:42:45 -0500 | [diff] [blame] | 1241 | int numHandlers = targets1.size(); |
| 1242 | if (numHandlers != targets2.size()) { |
| 1243 | return false; |
| 1244 | } |
Andrew Grieve | 24f16ea | 2024-01-23 09:36:03 -0500 | [diff] [blame] | 1245 | if (numHandlers == 0) { |
| 1246 | return true; |
| 1247 | } |
| 1248 | |
| 1249 | List<DexType> guards1 = catchHandlers.getGuards(); |
| 1250 | List<DexType> guards2 = other.catchHandlers.getGuards(); |
Andrew Grieve | 712a8ca | 2024-01-18 15:42:45 -0500 | [diff] [blame] | 1251 | for (int i = 0; i < numHandlers; ++i) { |
| 1252 | BasicBlock catchBlock1 = successors.get(targets1.get(i)); |
| 1253 | BasicBlock catchBlock2 = other.successors.get(targets2.get(i)); |
Andrew Grieve | 24f16ea | 2024-01-23 09:36:03 -0500 | [diff] [blame] | 1254 | BasicBlock trampolineTarget = catchBlock1.getExceptionTrampolineTarget(); |
| 1255 | if (trampolineTarget == null |
| 1256 | || trampolineTarget != catchBlock2.getExceptionTrampolineTarget() |
| 1257 | || guards1.get(i).isNotIdenticalTo(guards2.get(i))) { |
Andrew Grieve | 712a8ca | 2024-01-18 15:42:45 -0500 | [diff] [blame] | 1258 | return false; |
| 1259 | } |
| 1260 | } |
| 1261 | return true; |
| 1262 | } |
| 1263 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1264 | public void clearCurrentDefinitions() { |
| 1265 | currentDefinitions = null; |
| 1266 | for (Phi phi : getPhis()) { |
| 1267 | phi.clearDefinitionsUsers(); |
| 1268 | } |
| 1269 | } |
| 1270 | |
| 1271 | // The proper incoming register for a catch successor (that is otherwise shadowed by the out-value |
| 1272 | // of a throwing instruction) is stored at the negative register-index in the definitions map. |
| 1273 | // (See readCurrentDefinition/writeCurrentDefinition/updateCurrentDefinition). |
Mikaël Peltier | 2881245 | 2017-07-27 18:00:43 +0200 | [diff] [blame] | 1274 | private static int onThrowValueRegister(int register) { |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1275 | return -(register + 1); |
| 1276 | } |
| 1277 | |
| 1278 | private Value readOnThrowValue(int register, EdgeType readingEdge) { |
| 1279 | if (readingEdge == EdgeType.EXCEPTIONAL) { |
| 1280 | return currentDefinitions.get(onThrowValueRegister(register)); |
| 1281 | } |
| 1282 | return null; |
| 1283 | } |
| 1284 | |
| 1285 | private boolean isOnThrowValue(int register, EdgeType readingEdge) { |
| 1286 | return readOnThrowValue(register, readingEdge) != null; |
| 1287 | } |
| 1288 | |
| 1289 | public Value readCurrentDefinition(int register, EdgeType readingEdge) { |
| 1290 | // If the block reading the current definition is a catch successor, then we must return the |
| 1291 | // previous value of the throwing-instructions outgoing register if any. |
| 1292 | Value result = readOnThrowValue(register, readingEdge); |
| 1293 | if (result != null) { |
| 1294 | return result == Value.UNDEFINED ? null : result; |
| 1295 | } |
| 1296 | return currentDefinitions.get(register); |
| 1297 | } |
| 1298 | |
Ian Zerny | 658e86e | 2017-09-08 11:16:52 +0200 | [diff] [blame] | 1299 | public void replaceCurrentDefinitions(Value oldValue, Value newValue) { |
| 1300 | assert oldValue.definition.getBlock() == this; |
| 1301 | assert !oldValue.isUsed(); |
Mathias Rav | 1298d07 | 2018-04-16 16:08:05 +0200 | [diff] [blame] | 1302 | for (Entry<Integer, Value> entry : currentDefinitions.entrySet()) { |
| 1303 | if (entry.getValue() == oldValue) { |
| 1304 | if (oldValue.isPhi()) { |
| 1305 | oldValue.asPhi().removeDefinitionsUser(currentDefinitions); |
| 1306 | } |
| 1307 | entry.setValue(newValue); |
| 1308 | if (newValue.isPhi()) { |
| 1309 | newValue.asPhi().addDefinitionsUser(currentDefinitions); |
| 1310 | } |
| 1311 | } |
| 1312 | } |
Ian Zerny | 658e86e | 2017-09-08 11:16:52 +0200 | [diff] [blame] | 1313 | } |
| 1314 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1315 | public void updateCurrentDefinition(int register, Value value, EdgeType readingEdge) { |
| 1316 | // If the reading/writing block is a catch successor, possibly update the on-throw value. |
| 1317 | if (isOnThrowValue(register, readingEdge)) { |
| 1318 | register = onThrowValueRegister(register); |
| 1319 | } |
| 1320 | // We keep track of all users of phis so that we can update all users during |
| 1321 | // trivial phi elimination. We only rewrite phi values during IR construction, so |
| 1322 | // we only need to record definition users for phis. |
| 1323 | Value previousValue = currentDefinitions.get(register); |
| 1324 | if (value.isPhi()) { |
| 1325 | value.asPhi().addDefinitionsUser(currentDefinitions); |
| 1326 | } |
| 1327 | assert verifyOnThrowWrite(register); |
| 1328 | currentDefinitions.put(register, value); |
| 1329 | // We have replaced one occurrence of value in currentDefinitions. There could be |
| 1330 | // other occurrences. We only remove currentDefinitions from the set of users |
| 1331 | // of the phi if we have removed all occurrences. |
| 1332 | if (previousValue != null && |
| 1333 | previousValue.isPhi() && |
| 1334 | !currentDefinitions.values().contains(previousValue)) { |
| 1335 | previousValue.asPhi().removeDefinitionsUser(currentDefinitions); |
| 1336 | } |
| 1337 | } |
| 1338 | |
| 1339 | public void writeCurrentDefinition(int register, Value value, ThrowingInfo throwing) { |
| 1340 | // If this write is dependent on not throwing, we move the existing value to its negative index |
| 1341 | // so that it can be read by catch successors. |
| 1342 | if (throwing == ThrowingInfo.CAN_THROW) { |
| 1343 | Value previous = currentDefinitions.get(register); |
| 1344 | assert verifyOnThrowWrite(register); |
| 1345 | currentDefinitions.put(onThrowValueRegister(register), |
| 1346 | previous == null ? Value.UNDEFINED : previous); |
| 1347 | } |
| 1348 | updateCurrentDefinition(register, value, EdgeType.NON_EDGE); |
| 1349 | } |
| 1350 | |
| 1351 | public void filledPredecessor(IRBuilder builder) { |
| 1352 | assert unfilledPredecessorsCount > 0; |
| 1353 | if (--unfilledPredecessorsCount == 0) { |
| 1354 | assert estimatedPredecessorsCount == predecessors.size(); |
Mads Ager | ea9abac | 2017-06-02 14:44:20 +0200 | [diff] [blame] | 1355 | for (Entry<Integer, Phi> entry : incompletePhis.entrySet()) { |
| 1356 | int register = entry.getKey(); |
| 1357 | if (register < 0) { |
| 1358 | register = onThrowValueRegister(register); |
| 1359 | } |
| 1360 | entry.getValue().addOperands(builder, register); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1361 | } |
| 1362 | sealed = true; |
| 1363 | incompletePhis.clear(); |
| 1364 | } |
| 1365 | } |
| 1366 | |
| 1367 | public EdgeType getEdgeType(BasicBlock successor) { |
| 1368 | assert successors.indexOf(successor) >= 0; |
Mads Ager | c52c353 | 2017-06-14 11:54:02 +0200 | [diff] [blame] | 1369 | return hasCatchSuccessor(successor) ? EdgeType.EXCEPTIONAL : EdgeType.NORMAL; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1370 | } |
| 1371 | |
Mads Ager | c52c353 | 2017-06-14 11:54:02 +0200 | [diff] [blame] | 1372 | public boolean hasCatchSuccessor(BasicBlock block) { |
Andrew Grieve | 38e77b9 | 2024-01-19 13:53:08 -0500 | [diff] [blame] | 1373 | int numberOfExceptionalSuccessors = numberOfExceptionalSuccessors(); |
| 1374 | if (numberOfExceptionalSuccessors == 0) { |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1375 | return false; |
| 1376 | } |
Andrew Grieve | 38e77b9 | 2024-01-19 13:53:08 -0500 | [diff] [blame] | 1377 | int blockIndex = successors.indexOf(block); |
| 1378 | return blockIndex >= 0 && blockIndex < numberOfExceptionalSuccessors; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1379 | } |
| 1380 | |
| 1381 | public int guardsForCatchSuccessor(BasicBlock block) { |
Mads Ager | c52c353 | 2017-06-14 11:54:02 +0200 | [diff] [blame] | 1382 | assert hasCatchSuccessor(block); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1383 | int index = successors.indexOf(block); |
| 1384 | int count = 0; |
| 1385 | for (int handler : catchHandlers.getAllTargets()) { |
| 1386 | if (handler == index) { |
| 1387 | count++; |
| 1388 | } |
| 1389 | } |
| 1390 | assert count > 0; |
| 1391 | return count; |
| 1392 | } |
| 1393 | |
| 1394 | public boolean isSealed() { |
| 1395 | return sealed; |
| 1396 | } |
| 1397 | |
| 1398 | public void addIncompletePhi(int register, Phi phi, EdgeType readingEdge) { |
| 1399 | if (isOnThrowValue(register, readingEdge)) { |
| 1400 | register = onThrowValueRegister(register); |
| 1401 | } |
| 1402 | assert !incompletePhis.containsKey(register); |
| 1403 | incompletePhis.put(register, phi); |
| 1404 | } |
| 1405 | |
| 1406 | public boolean hasIncompletePhis() { |
| 1407 | return !incompletePhis.isEmpty(); |
| 1408 | } |
| 1409 | |
| 1410 | public Collection<Integer> getIncompletePhiRegisters() { |
| 1411 | return incompletePhis.keySet(); |
| 1412 | } |
| 1413 | |
Mikaël Peltier | 2881245 | 2017-07-27 18:00:43 +0200 | [diff] [blame] | 1414 | private static void appendBasicBlockList( |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1415 | StringBuilder builder, List<BasicBlock> list, Function<BasicBlock, String> postfix) { |
| 1416 | if (list.size() > 0) { |
| 1417 | for (BasicBlock block : list) { |
Mathias Rav | e905850 | 2018-05-15 10:13:49 +0200 | [diff] [blame] | 1418 | builder.append(block.getNumberAsString()); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1419 | builder.append(postfix.apply(block)); |
Mikaël Peltier | 2881245 | 2017-07-27 18:00:43 +0200 | [diff] [blame] | 1420 | builder.append(' '); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1421 | } |
| 1422 | } else { |
Mikaël Peltier | 2881245 | 2017-07-27 18:00:43 +0200 | [diff] [blame] | 1423 | builder.append('-'); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1424 | } |
| 1425 | } |
| 1426 | |
| 1427 | @Override |
| 1428 | public String toString() { |
| 1429 | return toDetailedString(); |
| 1430 | } |
| 1431 | |
| 1432 | public String toSimpleString() { |
| 1433 | return number < 0 ? super.toString() : ("block " + number); |
| 1434 | } |
| 1435 | |
| 1436 | private String predecessorPostfix(BasicBlock block) { |
Mads Ager | c52c353 | 2017-06-14 11:54:02 +0200 | [diff] [blame] | 1437 | if (hasCatchSuccessor(block)) { |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1438 | return new String(new char[guardsForCatchSuccessor(block)]).replace("\0", "*"); |
| 1439 | } |
| 1440 | return ""; |
| 1441 | } |
| 1442 | |
Ian Zerny | 31054bb | 2017-10-10 12:12:53 +0200 | [diff] [blame] | 1443 | private static int digits(int number) { |
| 1444 | return (int) Math.ceil(Math.log10(number + 1)); |
| 1445 | } |
| 1446 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1447 | public String toDetailedString() { |
| 1448 | StringBuilder builder = new StringBuilder(); |
| 1449 | builder.append("block "); |
| 1450 | builder.append(number); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1451 | builder.append(", pred-counts: " + predecessors.size()); |
| 1452 | if (unfilledPredecessorsCount > 0) { |
| 1453 | builder.append(" (" + unfilledPredecessorsCount + " unfilled)"); |
| 1454 | } |
| 1455 | builder.append(", succ-count: " + successors.size()); |
| 1456 | builder.append(", filled: " + isFilled()); |
| 1457 | builder.append(", sealed: " + isSealed()); |
Mikaël Peltier | 2881245 | 2017-07-27 18:00:43 +0200 | [diff] [blame] | 1458 | builder.append('\n'); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1459 | builder.append("predecessors: "); |
| 1460 | appendBasicBlockList(builder, predecessors, b -> ""); |
Mikaël Peltier | 2881245 | 2017-07-27 18:00:43 +0200 | [diff] [blame] | 1461 | builder.append('\n'); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1462 | builder.append("successors: "); |
| 1463 | appendBasicBlockList(builder, successors, this::predecessorPostfix); |
| 1464 | if (successors.size() > 0) { |
| 1465 | builder.append(" ("); |
| 1466 | if (hasCatchHandlers()) { |
| 1467 | builder.append(catchHandlers.size()); |
| 1468 | } else { |
| 1469 | builder.append("no"); |
| 1470 | } |
| 1471 | builder.append(" try/catch successors)"); |
| 1472 | } |
Mikaël Peltier | 2881245 | 2017-07-27 18:00:43 +0200 | [diff] [blame] | 1473 | builder.append('\n'); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1474 | if (phis != null && phis.size() > 0) { |
| 1475 | for (Phi phi : phis) { |
| 1476 | builder.append(phi.printPhi()); |
| 1477 | if (incompletePhis.values().contains(phi)) { |
| 1478 | builder.append(" (incomplete)"); |
| 1479 | } |
Mikaël Peltier | 2881245 | 2017-07-27 18:00:43 +0200 | [diff] [blame] | 1480 | builder.append('\n'); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1481 | } |
| 1482 | } else { |
| 1483 | builder.append("no phis\n"); |
| 1484 | } |
Ian Zerny | 5d16b09 | 2017-07-06 10:48:15 +0200 | [diff] [blame] | 1485 | if (localsAtEntry != null) { |
| 1486 | builder.append("locals: "); |
| 1487 | StringUtils.append(builder, localsAtEntry.int2ReferenceEntrySet(), ", ", BraceType.NONE); |
Mikaël Peltier | 2881245 | 2017-07-27 18:00:43 +0200 | [diff] [blame] | 1488 | builder.append('\n'); |
Ian Zerny | 5d16b09 | 2017-07-06 10:48:15 +0200 | [diff] [blame] | 1489 | } |
Ian Zerny | 31054bb | 2017-10-10 12:12:53 +0200 | [diff] [blame] | 1490 | int lineColumn = 0; |
| 1491 | int numberColumn = 0; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1492 | for (Instruction instruction : instructions) { |
Mathias Rav | e905850 | 2018-05-15 10:13:49 +0200 | [diff] [blame] | 1493 | lineColumn = Math.max(lineColumn, instruction.getPositionAsString().length()); |
Ian Zerny | 31054bb | 2017-10-10 12:12:53 +0200 | [diff] [blame] | 1494 | numberColumn = Math.max(numberColumn, digits(instruction.getNumber())); |
| 1495 | } |
Mathias Rav | e905850 | 2018-05-15 10:13:49 +0200 | [diff] [blame] | 1496 | String currentPosition = null; |
Ian Zerny | 31054bb | 2017-10-10 12:12:53 +0200 | [diff] [blame] | 1497 | for (Instruction instruction : instructions) { |
| 1498 | if (lineColumn > 0) { |
| 1499 | String line = ""; |
Mathias Rav | e905850 | 2018-05-15 10:13:49 +0200 | [diff] [blame] | 1500 | if (!instruction.getPositionAsString().equals(currentPosition)) { |
| 1501 | line = currentPosition = instruction.getPositionAsString(); |
Ian Zerny | 31054bb | 2017-10-10 12:12:53 +0200 | [diff] [blame] | 1502 | } |
| 1503 | StringUtils.appendLeftPadded(builder, line, lineColumn + 1); |
| 1504 | builder.append(": "); |
| 1505 | } |
| 1506 | StringUtils.appendLeftPadded(builder, "" + instruction.getNumber(), numberColumn + 1); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1507 | builder.append(": "); |
Ian Zerny | 31054bb | 2017-10-10 12:12:53 +0200 | [diff] [blame] | 1508 | builder.append(instruction.toString()); |
Ian Zerny | d73c0b4 | 2017-09-07 07:38:42 +0200 | [diff] [blame] | 1509 | if (DebugLocalInfo.PRINT_LEVEL != PrintLevel.NONE) { |
Ian Zerny | 2b1dae6 | 2020-05-28 09:44:55 +0200 | [diff] [blame] | 1510 | if (!instruction.getDebugValues().isEmpty()) { |
| 1511 | builder.append(" [end: "); |
| 1512 | StringUtils.append(builder, instruction.getDebugValues(), ", ", BraceType.NONE); |
| 1513 | builder.append("]"); |
Ian Zerny | d73c0b4 | 2017-09-07 07:38:42 +0200 | [diff] [blame] | 1514 | } |
Ian Zerny | d73c0b4 | 2017-09-07 07:38:42 +0200 | [diff] [blame] | 1515 | } |
| 1516 | builder.append("\n"); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1517 | } |
| 1518 | return builder.toString(); |
| 1519 | } |
| 1520 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1521 | /** |
Stephan Herhut | 2ecfe14 | 2017-06-01 14:24:01 +0200 | [diff] [blame] | 1522 | * Remove an instruction. |
| 1523 | */ |
| 1524 | public void removeInstruction(Instruction toRemove) { |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 1525 | instructions.removeIgnoreValues(toRemove); |
Stephan Herhut | 2ecfe14 | 2017-06-01 14:24:01 +0200 | [diff] [blame] | 1526 | } |
| 1527 | |
| 1528 | /** |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1529 | * Create a new basic block with a single goto instruction. |
| 1530 | * |
Ian Zerny | 0dba4dd | 2018-09-26 14:48:22 +0200 | [diff] [blame] | 1531 | * <p>The constructed basic block has no predecessors and has one successor which is the target |
| 1532 | * block. |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1533 | * |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1534 | * @param blockNumber the block number of the goto block |
Stephan Herhut | 6b06bfb | 2017-10-26 11:29:51 +0200 | [diff] [blame] | 1535 | * @param target the target of the goto block |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1536 | */ |
Christoffer Quist Adamsen | bfe352b | 2019-08-03 10:23:29 +0200 | [diff] [blame] | 1537 | public static BasicBlock createGotoBlock( |
| 1538 | int blockNumber, Position position, IRMetadata metadata, BasicBlock target) { |
| 1539 | BasicBlock block = createGotoBlock(blockNumber, position, metadata); |
Christoffer Quist Adamsen | 6c71cd9 | 2018-12-12 09:56:03 +0100 | [diff] [blame] | 1540 | block.getMutableSuccessors().add(target); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1541 | return block; |
| 1542 | } |
| 1543 | |
| 1544 | /** |
| 1545 | * Create a new basic block with a single goto instruction. |
| 1546 | * |
| 1547 | * <p>The constructed basic block has no predecessors and no successors. |
| 1548 | * |
| 1549 | * @param blockNumber the block number of the goto block |
| 1550 | */ |
Christoffer Quist Adamsen | bfe352b | 2019-08-03 10:23:29 +0200 | [diff] [blame] | 1551 | public static BasicBlock createGotoBlock( |
| 1552 | int blockNumber, Position position, IRMetadata metadata) { |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 1553 | BasicBlock block = new BasicBlock(metadata); |
Christoffer Quist Adamsen | bfe352b | 2019-08-03 10:23:29 +0200 | [diff] [blame] | 1554 | block.add(new Goto(), metadata); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1555 | block.close(null); |
| 1556 | block.setNumber(blockNumber); |
Ian Zerny | 0dba4dd | 2018-09-26 14:48:22 +0200 | [diff] [blame] | 1557 | block.entry().setPosition(position); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1558 | return block; |
| 1559 | } |
| 1560 | |
Søren Gjesse | e3ca97d | 2017-06-07 11:21:37 +0200 | [diff] [blame] | 1561 | /** |
| 1562 | * Create a new basic block with a single if instruction. |
| 1563 | * |
| 1564 | * <p>The constructed basic block has no predecessors and no successors. |
| 1565 | * |
Søren Gjesse | 3cd05ab | 2017-08-22 10:26:39 +0200 | [diff] [blame] | 1566 | * @param blockNumber the block number of the block |
| 1567 | * @param theIf the if instruction |
Søren Gjesse | e3ca97d | 2017-06-07 11:21:37 +0200 | [diff] [blame] | 1568 | */ |
Christoffer Quist Adamsen | bfe352b | 2019-08-03 10:23:29 +0200 | [diff] [blame] | 1569 | public static BasicBlock createIfBlock(int blockNumber, If theIf, IRMetadata metadata) { |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 1570 | BasicBlock block = new BasicBlock(metadata); |
Christoffer Quist Adamsen | bfe352b | 2019-08-03 10:23:29 +0200 | [diff] [blame] | 1571 | block.add(theIf, metadata); |
Søren Gjesse | e3ca97d | 2017-06-07 11:21:37 +0200 | [diff] [blame] | 1572 | block.close(null); |
| 1573 | block.setNumber(blockNumber); |
| 1574 | return block; |
| 1575 | } |
| 1576 | |
Søren Gjesse | 3cd05ab | 2017-08-22 10:26:39 +0200 | [diff] [blame] | 1577 | /** |
| 1578 | * Create a new basic block with an instruction followed by an if instruction. |
| 1579 | * |
| 1580 | * <p>The constructed basic block has no predecessors and no successors. |
| 1581 | * |
| 1582 | * @param blockNumber the block number of the block |
| 1583 | * @param theIf the if instruction |
Christoffer Quist Adamsen | 4929264 | 2019-08-01 15:52:06 +0200 | [diff] [blame] | 1584 | * @param instructions the instructions to place before the if instruction |
Søren Gjesse | 3cd05ab | 2017-08-22 10:26:39 +0200 | [diff] [blame] | 1585 | */ |
Christoffer Quist Adamsen | bfe352b | 2019-08-03 10:23:29 +0200 | [diff] [blame] | 1586 | public static BasicBlock createIfBlock( |
| 1587 | int blockNumber, If theIf, IRMetadata metadata, Instruction... instructions) { |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 1588 | BasicBlock block = new BasicBlock(metadata); |
Christoffer Quist Adamsen | d3cf0ce | 2019-06-19 15:38:31 +0200 | [diff] [blame] | 1589 | for (Instruction instruction : instructions) { |
Christoffer Quist Adamsen | bfe352b | 2019-08-03 10:23:29 +0200 | [diff] [blame] | 1590 | block.add(instruction, metadata); |
Christoffer Quist Adamsen | d3cf0ce | 2019-06-19 15:38:31 +0200 | [diff] [blame] | 1591 | } |
Christoffer Quist Adamsen | bfe352b | 2019-08-03 10:23:29 +0200 | [diff] [blame] | 1592 | block.add(theIf, metadata); |
Søren Gjesse | 3cd05ab | 2017-08-22 10:26:39 +0200 | [diff] [blame] | 1593 | block.close(null); |
| 1594 | block.setNumber(blockNumber); |
| 1595 | return block; |
| 1596 | } |
| 1597 | |
Christoffer Quist Adamsen | bfe352b | 2019-08-03 10:23:29 +0200 | [diff] [blame] | 1598 | public static BasicBlock createSwitchBlock( |
| 1599 | int blockNumber, IntSwitch theSwitch, IRMetadata metadata) { |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 1600 | BasicBlock block = new BasicBlock(metadata); |
Christoffer Quist Adamsen | bfe352b | 2019-08-03 10:23:29 +0200 | [diff] [blame] | 1601 | block.add(theSwitch, metadata); |
Søren Gjesse | 3cd05ab | 2017-08-22 10:26:39 +0200 | [diff] [blame] | 1602 | block.close(null); |
| 1603 | block.setNumber(blockNumber); |
| 1604 | return block; |
| 1605 | } |
| 1606 | |
Jinseong Jeon | b7f8532 | 2018-10-05 12:23:36 -0700 | [diff] [blame] | 1607 | public static BasicBlock createRethrowBlock( |
Christoffer Quist Adamsen | 446b931 | 2019-04-26 11:18:09 +0200 | [diff] [blame] | 1608 | IRCode code, Position position, DexType guard, AppView<?> appView) { |
Christoffer Quist Adamsen | e0302c2 | 2020-03-20 14:39:56 +0100 | [diff] [blame] | 1609 | TypeElement guardTypeLattice = |
| 1610 | TypeElement.fromDexType(guard, Nullability.definitelyNotNull(), appView); |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 1611 | BasicBlock block = new BasicBlock(code.metadata()); |
Christoffer Quist Adamsen | 2e7d7e4 | 2019-03-12 15:43:07 +0100 | [diff] [blame] | 1612 | MoveException moveException = |
Christoffer Quist Adamsen | 26a3f78 | 2023-10-12 10:44:53 +0200 | [diff] [blame] | 1613 | new MoveException(code.createValue(guardTypeLattice), guard, appView.options()); |
Mads Ager | b2a1894 | 2018-08-23 13:37:02 +0200 | [diff] [blame] | 1614 | moveException.setPosition(position); |
| 1615 | Throw throwInstruction = new Throw(moveException.outValue); |
| 1616 | throwInstruction.setPosition(position); |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 1617 | block.instructions.addLast(moveException); |
| 1618 | block.instructions.addLast(throwInstruction); |
Mads Ager | b2a1894 | 2018-08-23 13:37:02 +0200 | [diff] [blame] | 1619 | block.close(null); |
Morten Krogh-Jespersen | b6ec9ed | 2020-08-07 12:59:26 +0200 | [diff] [blame] | 1620 | block.setNumber(code.getNextBlockNumber()); |
Mads Ager | b2a1894 | 2018-08-23 13:37:02 +0200 | [diff] [blame] | 1621 | return block; |
| 1622 | } |
| 1623 | |
Christoffer Quist Adamsen | e77fd71 | 2021-06-18 10:57:03 +0200 | [diff] [blame] | 1624 | public boolean isInstructionBeforeThrowingInstruction(Instruction instruction) { |
| 1625 | assert instruction.getBlock() == this; |
| 1626 | for (Instruction candidate : getInstructions()) { |
| 1627 | if (candidate == instruction) { |
| 1628 | return true; |
| 1629 | } |
| 1630 | if (candidate.instructionTypeCanThrow()) { |
| 1631 | return false; |
| 1632 | } |
| 1633 | } |
| 1634 | throw new Unreachable(); |
| 1635 | } |
| 1636 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1637 | public boolean isTrivialGoto() { |
| 1638 | return instructions.size() == 1 && exit().isGoto(); |
| 1639 | } |
| 1640 | |
Christoffer Quist Adamsen | 6347ab5 | 2019-06-19 15:30:17 +0200 | [diff] [blame] | 1641 | // Go backwards in the control flow graph until a block that is not a trivial goto block is found, |
| 1642 | // or a block that does not have a unique predecessor is found. Returns null if the goto chain is |
| 1643 | // cyclic. |
| 1644 | public BasicBlock startOfGotoChain() { |
| 1645 | // See Floyd's cycle-finding algorithm for reference. |
| 1646 | BasicBlock hare = this; |
| 1647 | BasicBlock tortuous = this; |
| 1648 | boolean advance = false; |
| 1649 | while (hare.isTrivialGoto() && hare.hasUniquePredecessor()) { |
| 1650 | hare = hare.getUniquePredecessor(); |
| 1651 | tortuous = advance ? tortuous.getUniquePredecessor() : tortuous; |
| 1652 | advance = !advance; |
| 1653 | if (hare == tortuous) { |
| 1654 | return null; |
| 1655 | } |
| 1656 | } |
| 1657 | return hare; |
| 1658 | } |
| 1659 | |
Ian Zerny | 31054bb | 2017-10-10 12:12:53 +0200 | [diff] [blame] | 1660 | // Find the final target from this goto block. Returns null if the goto chain is cyclic. |
| 1661 | public BasicBlock endOfGotoChain() { |
Denis Vnukov | 2801bfd | 2018-01-31 08:48:33 -0800 | [diff] [blame] | 1662 | // See Floyd's cycle-finding algorithm for reference. |
Ian Zerny | 31054bb | 2017-10-10 12:12:53 +0200 | [diff] [blame] | 1663 | BasicBlock hare = this; |
| 1664 | BasicBlock tortuous = this; |
| 1665 | boolean advance = false; |
| 1666 | while (hare.isTrivialGoto()) { |
| 1667 | hare = hare.exit().asGoto().getTarget(); |
| 1668 | tortuous = advance ? tortuous.exit().asGoto().getTarget() : tortuous; |
| 1669 | advance = !advance; |
| 1670 | if (hare == tortuous) { |
| 1671 | return null; |
| 1672 | } |
| 1673 | } |
| 1674 | return hare; |
| 1675 | } |
| 1676 | |
Tamas Kenez | 2f30be0 | 2018-02-28 11:11:00 +0100 | [diff] [blame] | 1677 | public boolean isSimpleAlwaysThrowingPath() { |
Denis Vnukov | 2801bfd | 2018-01-31 08:48:33 -0800 | [diff] [blame] | 1678 | // See Floyd's cycle-finding algorithm for reference. |
| 1679 | BasicBlock hare = this; |
| 1680 | BasicBlock tortuous = this; |
| 1681 | boolean advance = false; |
| 1682 | while (true) { |
| 1683 | List<BasicBlock> normalSuccessors = hare.getNormalSuccessors(); |
Denis Vnukov | 2801bfd | 2018-01-31 08:48:33 -0800 | [diff] [blame] | 1684 | if (normalSuccessors.size() > 1) { |
| 1685 | return false; |
| 1686 | } |
| 1687 | |
Denis Vnukov | 4dc50a3 | 2018-02-09 07:43:50 -0800 | [diff] [blame] | 1688 | if (normalSuccessors.size() == 0) { |
| 1689 | return hare.exit().isThrow(); |
| 1690 | } |
| 1691 | |
Denis Vnukov | 2801bfd | 2018-01-31 08:48:33 -0800 | [diff] [blame] | 1692 | hare = normalSuccessors.get(0); |
| 1693 | tortuous = advance ? tortuous.getNormalSuccessors().get(0) : tortuous; |
| 1694 | advance = !advance; |
| 1695 | if (hare == tortuous) { |
| 1696 | return false; |
| 1697 | } |
| 1698 | } |
| 1699 | } |
| 1700 | |
Ian Zerny | 31054bb | 2017-10-10 12:12:53 +0200 | [diff] [blame] | 1701 | public Position getPosition() { |
Ian Zerny | 0dba4dd | 2018-09-26 14:48:22 +0200 | [diff] [blame] | 1702 | return entry().getPosition(); |
Ian Zerny | 31054bb | 2017-10-10 12:12:53 +0200 | [diff] [blame] | 1703 | } |
| 1704 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1705 | public boolean hasOneNormalExit() { |
| 1706 | return successors.size() == 1 && exit().isGoto(); |
| 1707 | } |
| 1708 | |
| 1709 | public CatchHandlers<BasicBlock> getCatchHandlers() { |
| 1710 | if (!hasCatchHandlers()) { |
| 1711 | return CatchHandlers.EMPTY_BASIC_BLOCK; |
| 1712 | } |
| 1713 | List<BasicBlock> targets = ListUtils.map(catchHandlers.getAllTargets(), successors::get); |
| 1714 | return new CatchHandlers<>(catchHandlers.getGuards(), targets); |
| 1715 | } |
| 1716 | |
| 1717 | public CatchHandlers<Integer> getCatchHandlersWithSuccessorIndexes() { |
| 1718 | return catchHandlers; |
| 1719 | } |
| 1720 | |
| 1721 | public void clearCatchHandlers() { |
| 1722 | catchHandlers = CatchHandlers.EMPTY_INDICES; |
| 1723 | } |
| 1724 | |
| 1725 | public void transferCatchHandlers(BasicBlock other) { |
| 1726 | catchHandlers = other.catchHandlers; |
| 1727 | other.catchHandlers = CatchHandlers.EMPTY_INDICES; |
| 1728 | } |
| 1729 | |
Christoffer Quist Adamsen | a911b27 | 2018-08-17 14:35:51 +0200 | [diff] [blame] | 1730 | public int numberOfCatchHandlers() { |
| 1731 | return catchHandlers.size(); |
| 1732 | } |
| 1733 | |
| 1734 | public int numberOfThrowingInstructions() { |
| 1735 | int count = 0; |
| 1736 | for (Instruction instruction : getInstructions()) { |
| 1737 | if (instruction.instructionTypeCanThrow()) { |
| 1738 | count++; |
| 1739 | } |
| 1740 | } |
| 1741 | return count; |
| 1742 | } |
| 1743 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1744 | public boolean canThrow() { |
| 1745 | for (Instruction instruction : instructions) { |
| 1746 | if (instruction.instructionTypeCanThrow()) { |
| 1747 | return true; |
| 1748 | } |
| 1749 | } |
| 1750 | return false; |
| 1751 | } |
| 1752 | |
| 1753 | // A block can have at most one "on throw" value. |
| 1754 | private boolean verifyOnThrowWrite(int register) { |
| 1755 | if (register >= 0) { |
| 1756 | return true; |
| 1757 | } |
| 1758 | for (Integer other : currentDefinitions.keySet()) { |
| 1759 | assert other >= 0 || other == register; |
| 1760 | } |
| 1761 | return true; |
| 1762 | } |
| 1763 | |
| 1764 | // Verify that if this block has a throwing instruction none of the following instructions may |
| 1765 | // introduce an SSA value. Such values can lead to invalid uses since the values are not actually |
| 1766 | // visible to exceptional successors. |
| 1767 | private boolean verifyNoValuesAfterThrowingInstruction() { |
| 1768 | if (hasCatchHandlers()) { |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 1769 | for (Instruction instruction = getLastInstruction(); |
| 1770 | instruction != null; |
| 1771 | instruction = instruction.getPrev()) { |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1772 | if (instruction.instructionTypeCanThrow()) { |
| 1773 | return true; |
| 1774 | } |
| 1775 | assert instruction.outValue() == null; |
| 1776 | } |
| 1777 | } |
| 1778 | return true; |
| 1779 | } |
| 1780 | |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 1781 | public InstructionIterator iterator() { |
| 1782 | return instructions.iterator(); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1783 | } |
| 1784 | |
Christoffer Adamsen | 1024c28 | 2025-03-20 11:13:12 +0000 | [diff] [blame^] | 1785 | public BasicBlockInstructionListIterator iterator(Instruction instruction) { |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 1786 | return instructions.iterator(instruction); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1787 | } |
| 1788 | |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 1789 | public BasicBlockInstructionListIterator listIterator() { |
| 1790 | return new BasicBlockInstructionListIterator(this); |
Christoffer Quist Adamsen | 4929264 | 2019-08-01 15:52:06 +0200 | [diff] [blame] | 1791 | } |
| 1792 | |
Andrew Grieve | ee12534 | 2024-11-14 12:20:39 -0500 | [diff] [blame] | 1793 | // TODO(b/376663044): Convert uses of index to use Instruction instead. |
| 1794 | public BasicBlockInstructionListIterator listIterator(int index) { |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 1795 | return new BasicBlockInstructionListIterator(this, index); |
Christoffer Quist Adamsen | 4929264 | 2019-08-01 15:52:06 +0200 | [diff] [blame] | 1796 | } |
| 1797 | |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 1798 | /** Creates an instruction list iterator starting at <code>firstInstructionToReturn</code>. */ |
Andrew Grieve | ee12534 | 2024-11-14 12:20:39 -0500 | [diff] [blame] | 1799 | public BasicBlockInstructionListIterator listIterator(Instruction firstInstructionToReturn) { |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 1800 | return new BasicBlockInstructionListIterator(this, firstInstructionToReturn); |
Søren Gjesse | f9417d8 | 2017-07-06 14:14:41 +0200 | [diff] [blame] | 1801 | } |
| 1802 | |
| 1803 | /** |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1804 | * Creates a new empty block as a successor for this block. |
| 1805 | * |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 1806 | * <p>The new block will have all the normal successors of the original block. |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1807 | * |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 1808 | * <p>The catch successors are either on the original block or the new block depending on the |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1809 | * value of <code>keepCatchHandlers</code>. |
| 1810 | * |
Andrew Grieve | c18e93f | 2024-11-13 14:30:16 -0500 | [diff] [blame] | 1811 | * <p>A Goto will be added to link the blocks (either to the end of this block when <code> |
| 1812 | * firstInstructionOfNewBlock</code> is not null, or as the only instruction in the new block |
| 1813 | * otherwise. |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1814 | * |
| 1815 | * @param blockNumber block number for new block |
| 1816 | * @param keepCatchHandlers keep catch successors on the original block |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 1817 | * @param firstInstructionOfNewBlock this and all following are moved to the new block |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1818 | * @return the new block |
| 1819 | */ |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 1820 | BasicBlock createSplitBlock( |
| 1821 | int blockNumber, boolean keepCatchHandlers, Instruction firstInstructionOfNewBlock) { |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1822 | boolean hadCatchHandlers = hasCatchHandlers(); |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 1823 | BasicBlock newBlock = new BasicBlock(metadata); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1824 | newBlock.setNumber(blockNumber); |
| 1825 | |
| 1826 | // Copy all successors including catch handlers to the new block, and update predecessors. |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 1827 | newBlock.getMutableSuccessors().addAll(successors); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1828 | for (BasicBlock successor : newBlock.getSuccessors()) { |
| 1829 | successor.replacePredecessor(this, newBlock); |
| 1830 | } |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 1831 | getMutableSuccessors().clear(); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1832 | newBlock.catchHandlers = catchHandlers; |
| 1833 | catchHandlers = CatchHandlers.EMPTY_INDICES; |
| 1834 | |
| 1835 | // If the catch handlers should be kept on the original block move them back. |
| 1836 | if (keepCatchHandlers && hadCatchHandlers) { |
| 1837 | moveCatchHandlers(newBlock); |
| 1838 | } |
| 1839 | |
| 1840 | // Link the two blocks |
| 1841 | link(newBlock); |
| 1842 | |
Andrew Grieve | c18e93f | 2024-11-13 14:30:16 -0500 | [diff] [blame] | 1843 | Goto newGoto = new Goto(); |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 1844 | if (firstInstructionOfNewBlock != null) { |
| 1845 | newBlock.instructions.severFrom(firstInstructionOfNewBlock); |
Andrew Grieve | c18e93f | 2024-11-13 14:30:16 -0500 | [diff] [blame] | 1846 | newGoto.setPosition( |
| 1847 | firstInstructionOfNewBlock.getPrev() != null |
| 1848 | ? firstInstructionOfNewBlock.getPrev().getPosition() |
| 1849 | : firstInstructionOfNewBlock.getPosition()); |
| 1850 | instructions.addLast(newGoto); |
| 1851 | } else { |
| 1852 | newGoto.setPosition(Position.none()); |
| 1853 | newBlock.instructions.addLast(newGoto); |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 1854 | } |
| 1855 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1856 | // Mark the new block filled and sealed. |
| 1857 | newBlock.filled = true; |
| 1858 | newBlock.sealed = true; |
| 1859 | |
| 1860 | return newBlock; |
| 1861 | } |
| 1862 | |
| 1863 | /** |
Andrew Grieve | c18e93f | 2024-11-13 14:30:16 -0500 | [diff] [blame] | 1864 | * Split the block into two. The existing block will have all the instructions before the |
| 1865 | * |firstInstructionOfNewBlock, and the new block all the instructions after. A Goto will be added |
| 1866 | * either to the new block (if firstInstructionOfNewBlock is null), or to this block. |
| 1867 | * |
| 1868 | * <p>If the current block has catch handlers these catch handlers will be attached to the block |
| 1869 | * containing the throwing instruction after the split. |
| 1870 | * |
| 1871 | * @param code the IR code for the block this iterator originates from. |
| 1872 | * @param keepCatchHandlers whether to keep catch handlers on the original block. |
| 1873 | * @param firstInstructionOfNewBlock first instruction to put into the new block. Can be null. |
| 1874 | * @return Returns the new block. |
| 1875 | */ |
| 1876 | public BasicBlock split( |
| 1877 | IRCode code, boolean keepCatchHandlers, Instruction firstInstructionOfNewBlock) { |
| 1878 | List<BasicBlock> blocks = code.blocks; |
| 1879 | assert blocks.contains(this); |
| 1880 | assert firstInstructionOfNewBlock == null || firstInstructionOfNewBlock.block == this; |
| 1881 | |
| 1882 | // Prepare the new block, placing the exception handlers on the block with the throwing |
| 1883 | // instruction. |
| 1884 | BasicBlock newBlock = |
| 1885 | createSplitBlock(code.getNextBlockNumber(), keepCatchHandlers, firstInstructionOfNewBlock); |
| 1886 | |
| 1887 | blocks.add(blocks.indexOf(this) + 1, newBlock); |
| 1888 | return newBlock; |
| 1889 | } |
| 1890 | |
| 1891 | /** |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1892 | * Moves catch successors from `fromBlock` into this block. |
| 1893 | */ |
| 1894 | public void moveCatchHandlers(BasicBlock fromBlock) { |
| 1895 | List<BasicBlock> catchSuccessors = appendCatchHandlers(fromBlock); |
| 1896 | for (BasicBlock successor : catchSuccessors) { |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 1897 | fromBlock.getMutableSuccessors().remove(successor); |
Morten Krogh-Jespersen | 969699f | 2019-07-09 12:14:59 +0200 | [diff] [blame] | 1898 | successor.removePredecessor(fromBlock, null); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1899 | } |
| 1900 | fromBlock.catchHandlers = CatchHandlers.EMPTY_INDICES; |
| 1901 | } |
| 1902 | |
| 1903 | /** |
| 1904 | * Clone catch successors from `fromBlock` into this block. |
| 1905 | */ |
| 1906 | public void copyCatchHandlers( |
Mads Ager | b83918c | 2019-01-10 12:20:45 +0100 | [diff] [blame] | 1907 | IRCode code, |
| 1908 | ListIterator<BasicBlock> blockIterator, |
| 1909 | BasicBlock fromBlock, |
| 1910 | InternalOptions options) { |
Ian Zerny | c818a92 | 2019-03-04 10:38:55 +0100 | [diff] [blame] | 1911 | if (catchHandlers != null && catchHandlers.hasCatchAll(options.itemFactory)) { |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1912 | return; |
| 1913 | } |
| 1914 | List<BasicBlock> catchSuccessors = appendCatchHandlers(fromBlock); |
| 1915 | |
| 1916 | // After cloning is done all catch handler targets are referenced from both the |
| 1917 | // original and the newly created catch handlers. Thus, since we keep both of |
| 1918 | // them, we need to split appropriate edges to make sure every catch handler |
| 1919 | // target block has only one predecessor. |
| 1920 | // |
| 1921 | // Note that for each catch handler block target block we actually create two new blocks: |
| 1922 | // a copy of the original block and a new block to serve as a merging point for |
| 1923 | // the original and its copy. This actually simplifies things since we only need |
| 1924 | // one new phi to merge the two exception values, and all other phis don't need |
| 1925 | // to be changed. |
| 1926 | for (BasicBlock catchSuccessor : catchSuccessors) { |
Christoffer Quist Adamsen | a117f6e | 2020-12-23 20:01:38 +0100 | [diff] [blame] | 1927 | catchSuccessor.splitCriticalExceptionEdges(code, blockIterator, options); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1928 | } |
| 1929 | } |
| 1930 | |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1931 | /** |
Christoffer Quist Adamsen | bfe352b | 2019-08-03 10:23:29 +0200 | [diff] [blame] | 1932 | * Assumes that `this` block is a catch handler target (note that it does not have to start with |
| 1933 | * MoveException instruction, since the instruction can be removed by optimizations like dead code |
| 1934 | * remover. |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1935 | * |
Christoffer Quist Adamsen | bfe352b | 2019-08-03 10:23:29 +0200 | [diff] [blame] | 1936 | * <p>Introduces new blocks on all incoming edges and clones MoveException instruction to these |
| 1937 | * blocks if it exists. All exception values introduced in newly created blocks are combined in a |
| 1938 | * phi added to `this` block. |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1939 | * |
Christoffer Quist Adamsen | bfe352b | 2019-08-03 10:23:29 +0200 | [diff] [blame] | 1940 | * <p>Note that if there are any other phis defined on this block, they remain valid, since this |
| 1941 | * method does not affect incoming edges in any way, and just adds new blocks with MoveException |
| 1942 | * and Goto. |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1943 | */ |
Morten Krogh-Jespersen | b6ec9ed | 2020-08-07 12:59:26 +0200 | [diff] [blame] | 1944 | public void splitCriticalExceptionEdges( |
Christoffer Quist Adamsen | a117f6e | 2020-12-23 20:01:38 +0100 | [diff] [blame] | 1945 | IRCode code, ListIterator<BasicBlock> blockIterator, InternalOptions options) { |
Christoffer Quist Adamsen | 6c71cd9 | 2018-12-12 09:56:03 +0100 | [diff] [blame] | 1946 | List<BasicBlock> predecessors = getMutablePredecessors(); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1947 | boolean hasMoveException = entry().isMoveException(); |
Christoffer Quist Adamsen | e0302c2 | 2020-03-20 14:39:56 +0100 | [diff] [blame] | 1948 | TypeElement exceptionTypeLattice = null; |
Mads Ager | b83918c | 2019-01-10 12:20:45 +0100 | [diff] [blame] | 1949 | DexType exceptionType = null; |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1950 | MoveException move = null; |
Ian Zerny | 31054bb | 2017-10-10 12:12:53 +0200 | [diff] [blame] | 1951 | Position position = entry().getPosition(); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1952 | if (hasMoveException) { |
| 1953 | // Remove the move-exception instruction. |
| 1954 | move = entry().asMoveException(); |
Christoffer Quist Adamsen | 1916c35 | 2020-03-23 11:30:48 +0100 | [diff] [blame] | 1955 | exceptionTypeLattice = move.getOutType(); |
Mads Ager | b83918c | 2019-01-10 12:20:45 +0100 | [diff] [blame] | 1956 | exceptionType = move.getExceptionType(); |
Ian Zerny | fc58080 | 2017-08-22 11:02:13 +0200 | [diff] [blame] | 1957 | assert move.getDebugValues().isEmpty(); |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 1958 | instructions.removeIgnoreValues(move); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1959 | } |
| 1960 | // Create new predecessor blocks. |
Christoffer Quist Adamsen | a117f6e | 2020-12-23 20:01:38 +0100 | [diff] [blame] | 1961 | List<BasicBlock> newPredecessors = new ArrayList<>(predecessors.size()); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1962 | List<Value> values = new ArrayList<>(predecessors.size()); |
| 1963 | for (BasicBlock predecessor : predecessors) { |
Mads Ager | c52c353 | 2017-06-14 11:54:02 +0200 | [diff] [blame] | 1964 | if (!predecessor.hasCatchSuccessor(this)) { |
| 1965 | throw new CompilationError( |
| 1966 | "Invalid block structure: catch block reachable via non-exceptional flow."); |
| 1967 | } |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 1968 | BasicBlock newBlock = new BasicBlock(metadata); |
Morten Krogh-Jespersen | b6ec9ed | 2020-08-07 12:59:26 +0200 | [diff] [blame] | 1969 | newBlock.setNumber(code.getNextBlockNumber()); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1970 | newPredecessors.add(newBlock); |
| 1971 | if (hasMoveException) { |
Christoffer Quist Adamsen | 26a3f78 | 2023-10-12 10:44:53 +0200 | [diff] [blame] | 1972 | Value value = code.createValue(exceptionTypeLattice, move.getLocalInfo()); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1973 | values.add(value); |
Mads Ager | b83918c | 2019-01-10 12:20:45 +0100 | [diff] [blame] | 1974 | MoveException newMove = new MoveException(value, exceptionType, options); |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 1975 | newBlock.instructions.addLast(newMove); |
Ian Zerny | 31054bb | 2017-10-10 12:12:53 +0200 | [diff] [blame] | 1976 | newMove.setPosition(position); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1977 | } |
Ian Zerny | 0dba4dd | 2018-09-26 14:48:22 +0200 | [diff] [blame] | 1978 | Goto next = new Goto(); |
| 1979 | next.setPosition(position); |
Andrew Grieve | fa94735 | 2024-11-06 11:28:43 -0500 | [diff] [blame] | 1980 | newBlock.instructions.addLast(next); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1981 | newBlock.close(null); |
Christoffer Quist Adamsen | 6c71cd9 | 2018-12-12 09:56:03 +0100 | [diff] [blame] | 1982 | newBlock.getMutableSuccessors().add(this); |
| 1983 | newBlock.getMutablePredecessors().add(predecessor); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1984 | predecessor.replaceSuccessor(this, newBlock); |
Ian Zerny | f33d3ca | 2022-12-02 13:48:43 +0100 | [diff] [blame] | 1985 | if (blockIterator == null) { |
| 1986 | code.blocks.add(newBlock); |
| 1987 | } else { |
| 1988 | blockIterator.add(newBlock); |
| 1989 | } |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 1990 | } |
| 1991 | // Replace the blocks predecessors with the new ones. |
| 1992 | predecessors.clear(); |
| 1993 | predecessors.addAll(newPredecessors); |
| 1994 | // Insert a phi for the move-exception value. |
| 1995 | if (hasMoveException) { |
Ian Zerny | a30db87 | 2018-08-17 10:50:54 +0200 | [diff] [blame] | 1996 | Phi phi = |
| 1997 | new Phi( |
Christoffer Quist Adamsen | bfe352b | 2019-08-03 10:23:29 +0200 | [diff] [blame] | 1998 | code.valueNumberGenerator.next(), |
Ian Zerny | a30db87 | 2018-08-17 10:50:54 +0200 | [diff] [blame] | 1999 | this, |
Jinseong Jeon | b7f8532 | 2018-10-05 12:23:36 -0700 | [diff] [blame] | 2000 | exceptionTypeLattice, |
Ian Zerny | a30db87 | 2018-08-17 10:50:54 +0200 | [diff] [blame] | 2001 | move.getLocalInfo(), |
| 2002 | RegisterReadType.NORMAL); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 2003 | phi.addOperands(values); |
| 2004 | move.outValue().replaceUsers(phi); |
| 2005 | } |
| 2006 | } |
| 2007 | |
Stephan Herhut | 2ecfe14 | 2017-06-01 14:24:01 +0200 | [diff] [blame] | 2008 | /** |
| 2009 | * Append catch handlers from another block <code>fromBlock</code> (which must have catch |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 2010 | * handlers) to the catch handlers of this block. |
| 2011 | * |
| 2012 | * Note that after appending catch handlers their targets are referenced by both |
| 2013 | * <code>fromBlock</code> and <code>this</code> block, but no phis are inserted. For this reason |
| 2014 | * this method should only be called from either {@link #moveCatchHandlers} or |
| 2015 | * {@link #copyCatchHandlers} which know how to handle phis. |
| 2016 | * |
Mikaël Peltier | be71dbc | 2017-07-28 11:33:14 +0200 | [diff] [blame] | 2017 | * @return the catch successors that are reused in both blocks after appending. |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 2018 | */ |
| 2019 | private List<BasicBlock> appendCatchHandlers(BasicBlock fromBlock) { |
| 2020 | assert fromBlock.hasCatchHandlers(); |
| 2021 | |
| 2022 | List<Integer> prevCatchTargets = fromBlock.catchHandlers.getAllTargets(); |
| 2023 | List<DexType> prevCatchGuards = fromBlock.catchHandlers.getGuards(); |
| 2024 | List<BasicBlock> catchSuccessors = new ArrayList<>(); |
| 2025 | List<DexType> newCatchGuards = new ArrayList<>(); |
| 2026 | List<Integer> newCatchTargets = new ArrayList<>(); |
| 2027 | |
| 2028 | // First add existing catch handlers to the catch handler list. |
| 2029 | if (hasCatchHandlers()) { |
| 2030 | newCatchGuards.addAll(catchHandlers.getGuards()); |
| 2031 | newCatchTargets.addAll(catchHandlers.getAllTargets()); |
| 2032 | for (int newCatchTarget : newCatchTargets) { |
| 2033 | BasicBlock catchSuccessor = successors.get(newCatchTarget); |
| 2034 | if (!catchSuccessors.contains(catchSuccessor)) { |
| 2035 | catchSuccessors.add(catchSuccessor); |
| 2036 | } |
| 2037 | int index = catchSuccessors.indexOf(catchSuccessor); |
| 2038 | assert index == newCatchTarget; |
| 2039 | } |
| 2040 | } |
| 2041 | |
| 2042 | // This is the number of catch handlers which are already successors of this block. |
| 2043 | int formerCatchHandlersCount = catchSuccessors.size(); |
| 2044 | |
| 2045 | // Then add catch handlers from the other block to the catch handler list. |
| 2046 | for (int i = 0; i < prevCatchTargets.size(); i++) { |
| 2047 | int prevCatchTarget = prevCatchTargets.get(i); |
| 2048 | DexType prevCatchGuard = prevCatchGuards.get(i); |
Søren Gjesse | 315c053 | 2018-10-01 15:36:11 +0200 | [diff] [blame] | 2049 | if (newCatchGuards.contains(prevCatchGuard)) { |
| 2050 | continue; |
| 2051 | } |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 2052 | BasicBlock catchSuccessor = fromBlock.successors.get(prevCatchTarget); |
Christoffer Quist Adamsen | e7479ea | 2019-10-10 16:04:01 +0200 | [diff] [blame] | 2053 | // We assume that all the catch handlers targets has only one predecessor and, thus, no phis. |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 2054 | assert catchSuccessor.getPredecessors().size() == 1; |
| 2055 | assert catchSuccessor.getPhis().isEmpty(); |
| 2056 | |
| 2057 | int index = catchSuccessors.indexOf(catchSuccessor); |
| 2058 | if (index == -1) { |
| 2059 | catchSuccessors.add(catchSuccessor); |
| 2060 | index = catchSuccessors.size() - 1; |
| 2061 | } |
| 2062 | newCatchGuards.add(prevCatchGuard); |
| 2063 | newCatchTargets.add(index); |
| 2064 | } |
| 2065 | |
| 2066 | // Create the new successors list and link things up. |
Christoffer Quist Adamsen | 5fbf0d5 | 2018-12-21 08:24:47 +0100 | [diff] [blame] | 2067 | List<BasicBlock> successors = getMutableSuccessors(); |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 2068 | List<BasicBlock> formerSuccessors = new ArrayList<>(successors); |
| 2069 | successors.clear(); |
| 2070 | List<BasicBlock> sharedCatchSuccessors = new ArrayList<>(); |
| 2071 | for (int i = 0; i < catchSuccessors.size(); i++) { |
| 2072 | if (i < formerCatchHandlersCount) { |
| 2073 | // Former catch successors are just copied, as they are already linked. |
| 2074 | assert catchSuccessors.get(i).getPredecessors().contains(this); |
| 2075 | successors.add(catchSuccessors.get(i)); |
| 2076 | } else { |
| 2077 | // New catch successors are linked properly. |
| 2078 | assert !catchSuccessors.get(i).getPredecessors().contains(this); |
| 2079 | link(catchSuccessors.get(i)); |
| 2080 | sharedCatchSuccessors.add(catchSuccessors.get(i)); |
| 2081 | } |
| 2082 | } |
| 2083 | catchHandlers = new CatchHandlers<>(newCatchGuards, newCatchTargets); |
| 2084 | |
| 2085 | // Finally add the normal successor if any. |
| 2086 | int catchSuccessorsCount = successors.size(); |
| 2087 | for (BasicBlock formerSuccessor : formerSuccessors) { |
| 2088 | if (!successors.contains(formerSuccessor)) { |
| 2089 | assert !exit().isThrow(); |
| 2090 | successors.add(formerSuccessor); |
| 2091 | } |
| 2092 | } |
| 2093 | assert successors.size() == catchSuccessorsCount || !exit().isThrow(); |
| 2094 | |
| 2095 | return sharedCatchSuccessors; |
| 2096 | } |
Mikaël Peltier | e8b2b16 | 2017-11-08 08:52:25 +0100 | [diff] [blame] | 2097 | |
| 2098 | /** |
| 2099 | * Return true if there is a path from the current {@link BasicBlock} to {@code target} or if |
| 2100 | * {@code target} is the same block than the current {@link BasicBlock}. |
| 2101 | */ |
| 2102 | public boolean hasPathTo(BasicBlock target) { |
Søren Gjesse | e96740d | 2019-11-26 17:13:36 +0100 | [diff] [blame] | 2103 | Set<BasicBlock> visitedBlocks = Sets.newIdentityHashSet(); |
Mikaël Peltier | e8b2b16 | 2017-11-08 08:52:25 +0100 | [diff] [blame] | 2104 | ArrayDeque<BasicBlock> blocks = new ArrayDeque<>(); |
| 2105 | blocks.push(this); |
| 2106 | |
Denis Vnukov | d1fd13b | 2018-01-22 12:08:19 -0800 | [diff] [blame] | 2107 | while (!blocks.isEmpty()) { |
Mikaël Peltier | e8b2b16 | 2017-11-08 08:52:25 +0100 | [diff] [blame] | 2108 | BasicBlock block = blocks.pop(); |
| 2109 | if (block == target) { |
| 2110 | return true; |
| 2111 | } |
| 2112 | visitedBlocks.add(block); |
| 2113 | for (BasicBlock blockToVisit : block.getSuccessors()) { |
| 2114 | if (!visitedBlocks.contains(blockToVisit)) { |
| 2115 | blocks.push(blockToVisit); |
| 2116 | } |
| 2117 | } |
| 2118 | } |
| 2119 | |
| 2120 | return false; |
| 2121 | } |
mikaelpeltier | 99bca2f | 2017-12-22 14:49:17 +0100 | [diff] [blame] | 2122 | |
| 2123 | private static class PhiEquivalence extends Equivalence<Phi> { |
| 2124 | @Override |
| 2125 | protected boolean doEquivalent(Phi a, Phi b) { |
| 2126 | assert a.getBlock() == b.getBlock(); |
| 2127 | for (int i = 0; i < a.getOperands().size(); i++) { |
| 2128 | if (a.getOperand(i) != b.getOperand(i)) { |
| 2129 | return false; |
| 2130 | } |
| 2131 | } |
| 2132 | return true; |
| 2133 | } |
| 2134 | |
| 2135 | @Override |
| 2136 | protected int doHash(Phi phi) { |
| 2137 | int hash = 0; |
| 2138 | for (Value value : phi.getOperands()) { |
| 2139 | hash = hash * 13 + value.hashCode(); |
| 2140 | } |
| 2141 | return hash; |
| 2142 | } |
| 2143 | } |
| 2144 | |
Christoffer Quist Adamsen | d139da0 | 2023-09-05 19:05:13 +0200 | [diff] [blame] | 2145 | @SuppressWarnings("ReferenceEquality") |
mikaelpeltier | 99bca2f | 2017-12-22 14:49:17 +0100 | [diff] [blame] | 2146 | public void deduplicatePhis() { |
| 2147 | PhiEquivalence equivalence = new PhiEquivalence(); |
| 2148 | HashMap<Wrapper<Phi>, Phi> wrapper2phi = new HashMap<>(); |
| 2149 | Iterator<Phi> phiIt = phis.iterator(); |
| 2150 | while (phiIt.hasNext()) { |
| 2151 | Phi phi = phiIt.next(); |
| 2152 | Wrapper<Phi> key = equivalence.wrap(phi); |
| 2153 | Phi replacement = wrapper2phi.get(key); |
Ian Zerny | 0ca4b64 | 2020-05-26 21:10:46 +0200 | [diff] [blame] | 2154 | if (replacement == null) { |
mikaelpeltier | 99bca2f | 2017-12-22 14:49:17 +0100 | [diff] [blame] | 2155 | wrapper2phi.put(key, phi); |
Ian Zerny | 0ca4b64 | 2020-05-26 21:10:46 +0200 | [diff] [blame] | 2156 | continue; |
mikaelpeltier | 99bca2f | 2017-12-22 14:49:17 +0100 | [diff] [blame] | 2157 | } |
Ian Zerny | 0ca4b64 | 2020-05-26 21:10:46 +0200 | [diff] [blame] | 2158 | // Two phis may be duplicates but still differ in debug info. |
| 2159 | // For example, it may be that the one phi denotes the result of a local pre-increment, while |
| 2160 | // the other phi represents the modified local, e.g., cond ? ++x : x will give rise to: |
| 2161 | // v2 <- phi(v0(x), v1(x)) |
| 2162 | // v3(x) <- phi(v0(x), v1(x)) |
| 2163 | // where v2 is used as the result of the expression and v3 is the local slot value of x. |
| 2164 | // This should be replaced by a single phi. |
| 2165 | if (phi.getLocalInfo() != replacement.getLocalInfo()) { |
| 2166 | if (replacement.getLocalInfo() == null) { |
| 2167 | // The replacement must take over the debug info. |
| 2168 | replacement.setLocalInfo(phi.getLocalInfo()); |
| 2169 | } else if (phi.getLocalInfo() == null) { |
| 2170 | // The replacement already owns debug info. |
| 2171 | } else { |
| 2172 | // The phis define two distinct locals and cannot be de-duped. |
| 2173 | assert phi.hasLocalInfo() && replacement.hasLocalInfo(); |
| 2174 | continue; |
| 2175 | } |
| 2176 | } |
| 2177 | phi.replaceUsers(replacement); |
| 2178 | for (Value operand : phi.getOperands()) { |
| 2179 | operand.removePhiUser(phi); |
| 2180 | } |
| 2181 | phiIt.remove(); |
mikaelpeltier | 99bca2f | 2017-12-22 14:49:17 +0100 | [diff] [blame] | 2182 | } |
| 2183 | } |
Morten Krogh-Jespersen | bb340d1 | 2022-08-30 12:23:01 +0200 | [diff] [blame] | 2184 | |
| 2185 | public void registerUse(UseRegistry<?> registry, ProgramMethod method) { |
| 2186 | for (Instruction instruction : instructions) { |
| 2187 | instruction.registerUse(registry, method); |
| 2188 | if (registry.getTraversalContinuation().shouldBreak()) { |
| 2189 | return; |
| 2190 | } |
| 2191 | } |
| 2192 | for (DexType guard : catchHandlers.getGuards()) { |
| 2193 | registry.registerExceptionGuard(guard); |
| 2194 | if (registry.getTraversalContinuation().shouldBreak()) { |
| 2195 | return; |
| 2196 | } |
| 2197 | } |
| 2198 | } |
Mads Ager | 418d1ca | 2017-05-22 09:35:49 +0200 | [diff] [blame] | 2199 | } |