blob: c92685260a6fe4f85be8f81d1a49726946c20fdc [file] [log] [blame]
Mads Ager418d1ca2017-05-22 09:35:49 +02001// 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.
4package com.android.tools.r8.ir.code;
5
Christoffer Adamsen383bcd82018-04-26 14:23:31 +02006import static com.android.tools.r8.ir.code.IRCode.INSTRUCTION_NUMBER_DELTA;
Christoffer Quist Adamsenb55c98e2024-01-22 15:26:00 +01007import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
Christoffer Quist Adamsen4111aba2024-01-23 15:35:55 +01008import static com.google.common.base.Predicates.alwaysFalse;
Christoffer Adamsen383bcd82018-04-26 14:23:31 +02009
Mads Agerc52c3532017-06-14 11:54:02 +020010import com.android.tools.r8.errors.CompilationError;
Christoffer Quist Adamsene77fd712021-06-18 10:57:03 +020011import com.android.tools.r8.errors.Unreachable;
Christoffer Quist Adamsen2e7d7e42019-03-12 15:43:07 +010012import com.android.tools.r8.graph.AppView;
Ian Zerny5d16b092017-07-06 10:48:15 +020013import com.android.tools.r8.graph.DebugLocalInfo;
Ian Zernyd73c0b42017-09-07 07:38:42 +020014import com.android.tools.r8.graph.DebugLocalInfo.PrintLevel;
Christoffer Quist Adamsen95dd66b2018-06-11 09:12:45 +020015import com.android.tools.r8.graph.DexItemFactory;
Mads Ager418d1ca2017-05-22 09:35:49 +020016import com.android.tools.r8.graph.DexType;
Morten Krogh-Jespersenbb340d12022-08-30 12:23:01 +020017import com.android.tools.r8.graph.ProgramMethod;
18import com.android.tools.r8.graph.UseRegistry;
Christoffer Quist Adamsenc52a3ac2023-03-28 13:04:47 +020019import com.android.tools.r8.graph.lens.GraphLens;
Christoffer Quist Adamsen7e2a9d42023-03-28 13:05:04 +020020import com.android.tools.r8.graph.lens.NonIdentityGraphLens;
Morten Krogh-Jespersen88facd92020-09-22 14:51:27 +020021import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
Jinseong Jeonfd086002019-01-10 14:55:00 -080022import com.android.tools.r8.ir.analysis.type.Nullability;
Christoffer Quist Adamsene0302c22020-03-20 14:39:56 +010023import com.android.tools.r8.ir.analysis.type.TypeElement;
Ian Zernya30db872018-08-17 10:50:54 +020024import com.android.tools.r8.ir.code.Phi.RegisterReadType;
Mads Ager418d1ca2017-05-22 09:35:49 +020025import com.android.tools.r8.ir.conversion.DexBuilder;
26import com.android.tools.r8.ir.conversion.IRBuilder;
Christoffer Quist Adamsen4111aba2024-01-23 15:35:55 +010027import com.android.tools.r8.ir.optimize.AffectedValues;
Mads Agerb83918c2019-01-10 12:20:45 +010028import com.android.tools.r8.utils.InternalOptions;
Christoffer Quist Adamsenc406cdd2022-07-01 13:17:44 +020029import com.android.tools.r8.utils.IterableUtils;
Mads Ager418d1ca2017-05-22 09:35:49 +020030import com.android.tools.r8.utils.ListUtils;
31import com.android.tools.r8.utils.StringUtils;
Ian Zerny5d16b092017-07-06 10:48:15 +020032import com.android.tools.r8.utils.StringUtils.BraceType;
Christoffer Quist Adamsene1fa9de2022-04-22 12:56:21 +020033import com.android.tools.r8.utils.TraversalContinuation;
Christoffer Quist Adamsenf9273362022-06-16 15:14:06 +020034import com.android.tools.r8.utils.TriFunction;
mikaelpeltier99bca2f2017-12-22 14:49:17 +010035import com.google.common.base.Equivalence;
36import com.google.common.base.Equivalence.Wrapper;
Mads Ager418d1ca2017-05-22 09:35:49 +020037import com.google.common.collect.ImmutableList;
Søren Gjesse315c0532018-10-01 15:36:11 +020038import com.google.common.collect.ImmutableSet;
Christoffer Quist Adamsenb3489552019-10-15 13:02:13 +020039import com.google.common.collect.Sets;
Ian Zerny5d16b092017-07-06 10:48:15 +020040import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
Christoffer Quist Adamsen95dd66b2018-06-11 09:12:45 +020041import it.unimi.dsi.fastutil.ints.IntArrayList;
42import it.unimi.dsi.fastutil.ints.IntList;
Mikaël Peltiere8b2b162017-11-08 08:52:25 +010043import java.util.ArrayDeque;
Mads Ager418d1ca2017-05-22 09:35:49 +020044import java.util.ArrayList;
Mads Ager418d1ca2017-05-22 09:35:49 +020045import java.util.Collection;
Stephan Herhut2ecfe142017-06-01 14:24:01 +020046import java.util.Collections;
Christoffer Quist Adamsen95dd66b2018-06-11 09:12:45 +020047import java.util.Comparator;
Mads Ager418d1ca2017-05-22 09:35:49 +020048import java.util.HashMap;
mikaelpeltier99bca2f2017-12-22 14:49:17 +010049import java.util.Iterator;
Mads Ager418d1ca2017-05-22 09:35:49 +020050import java.util.List;
51import java.util.ListIterator;
52import java.util.Map;
Mads Agerea9abac2017-06-02 14:44:20 +020053import java.util.Map.Entry;
Christoffer Quist Adamsen684e2812019-10-07 13:49:05 +020054import java.util.NoSuchElementException;
Mads Ager418d1ca2017-05-22 09:35:49 +020055import java.util.Set;
Christoffer Quist Adamsen5fbf0d52018-12-21 08:24:47 +010056import java.util.WeakHashMap;
Christoffer Quist Adamsene1fa9de2022-04-22 12:56:21 +020057import java.util.function.BiFunction;
Mads Ager418d1ca2017-05-22 09:35:49 +020058import java.util.function.Consumer;
59import java.util.function.Function;
Christoffer Quist Adamsenc406cdd2022-07-01 13:17:44 +020060import java.util.function.Predicate;
Mads Ager418d1ca2017-05-22 09:35:49 +020061
62/**
63 * Basic block abstraction.
64 */
65public class BasicBlock {
66
Christoffer Quist Adamsen5fbf0d52018-12-21 08:24:47 +010067 public interface BasicBlockChangeListener {
68 void onSuccessorsMayChange(BasicBlock block);
69
70 void onPredecessorsMayChange(BasicBlock block);
71 }
72
Ian Zerny5d16b092017-07-06 10:48:15 +020073 private Int2ReferenceMap<DebugLocalInfo> localsAtEntry;
74
Christoffer Quist Adamsena015e372021-06-29 20:12:29 +020075 public boolean consistentBlockInstructions(boolean argumentsAllowed, boolean debug, boolean ssa) {
Mathias Rav99446b02018-05-03 12:37:38 +020076 for (Instruction instruction : getInstructions()) {
Ian Zerny0dba4dd2018-09-26 14:48:22 +020077 assert instruction.verifyValidPositionInfo(debug);
Mathias Rav99446b02018-05-03 12:37:38 +020078 assert instruction.getBlock() == this;
79 assert !instruction.isArgument() || argumentsAllowed;
80 assert !instruction.isDebugLocalRead() || !instruction.getDebugValues().isEmpty();
Christoffer Quist Adamsena015e372021-06-29 20:12:29 +020081 assert !instruction.isInitClass()
82 || consistentInitClassInstruction(instruction.asInitClass(), ssa);
Ian Zerny7271f712018-09-20 09:22:59 +020083 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 Adamsen7c368962018-05-04 08:33:58 +020090 if (!instruction.isArgument()) {
Mathias Rav99446b02018-05-03 12:37:38 +020091 argumentsAllowed = false;
92 }
93 }
94 return true;
95 }
96
Christoffer Quist Adamsena015e372021-06-29 20:12:29 +020097 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 Adamsen56004fa2023-07-03 20:02:12 +0200108 public boolean verifyTypes(
109 AppView<?> appView, ProgramMethod context, VerifyTypesHelper verifyTypesHelper) {
Morten Krogh-Jespersen88facd92020-09-22 14:51:27 +0200110 assert instructions.stream()
Christoffer Quist Adamsen56004fa2023-07-03 20:02:12 +0200111 .allMatch(instruction -> instruction.verifyTypes(appView, context, verifyTypesHelper));
Christoffer Quist Adamsen27676922018-10-08 10:57:05 +0200112 return true;
113 }
114
Ian Zerny5d16b092017-07-06 10:48:15 +0200115 public void setLocalsAtEntry(Int2ReferenceMap<DebugLocalInfo> localsAtEntry) {
116 this.localsAtEntry = localsAtEntry;
117 }
118
119 public Int2ReferenceMap<DebugLocalInfo> getLocalsAtEntry() {
120 return localsAtEntry;
121 }
122
Christoffer Adamsenfa3dc192025-03-18 08:18:10 +0000123 public void replaceLastInstruction(Instruction instruction) {
Andrew Grieveee125342024-11-14 12:20:39 -0500124 InstructionListIterator iterator = listIterator(getInstructions().size());
Stephan Herhut3f026d32017-08-07 14:58:13 +0200125 iterator.previous();
126 iterator.replaceCurrentInstruction(instruction);
127 }
128
Mads Ager418d1ca2017-05-22 09:35:49 +0200129 public enum ThrowingInfo {
Christoffer Quist Adamsen26ef5a92019-02-21 17:03:31 +0100130 NO_THROW,
Christoffer Quist Adamsene1fa9de2022-04-22 12:56:21 +0200131 CAN_THROW
Mads Ager418d1ca2017-05-22 09:35:49 +0200132 }
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 Peltier427a73a2017-11-30 08:57:20 +0100160
161 @Override
162 public String toString() {
163 return "Edge: " + first.getNumber() + " -> " + second.getNumber();
164 }
Mads Ager418d1ca2017-05-22 09:35:49 +0200165 }
166
167 private final List<BasicBlock> successors = new ArrayList<>();
168 private final List<BasicBlock> predecessors = new ArrayList<>();
169
Christoffer Quist Adamsen5fbf0d52018-12-21 08:24:47 +0100170 private Set<BasicBlockChangeListener> onControlFlowEdgesMayChangeListeners = null;
171
Mads Ager418d1ca2017-05-22 09:35:49 +0200172 // Catch handler information about which successors are catch handlers and what their guards are.
173 private CatchHandlers<Integer> catchHandlers = CatchHandlers.EMPTY_INDICES;
174
Andrew Grievefa947352024-11-06 11:28:43 -0500175 // Linked list of instructions belonging to this block.
176 private final InstructionList instructions = new InstructionList(this);
Ian Zerny9004d052023-03-02 20:15:36 +0100177
Mads Ager418d1ca2017-05-22 09:35:49 +0200178 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 Hertz39e884b2018-01-10 17:18:02 +0100193 private final Map<Integer, Phi> incompletePhis = new HashMap<>();
Mads Ager418d1ca2017-05-22 09:35:49 +0200194 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 Grievefa947352024-11-06 11:28:43 -0500203 // 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 Adamsene1fa9de2022-04-22 12:56:21 +0200220 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 Adamsenf9273362022-06-16 15:14:06 +0200272 TriFunction<? super BasicBlock, DexType, ? super CT, TraversalContinuation<BT, CT>> fn,
Christoffer Quist Adamsene1fa9de2022-04-22 12:56:21 +0200273 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 Adamsenf9273362022-06-16 15:14:06 +0200279 fn.apply(
280 successors.get(i),
281 catchHandlers.getGuard(i),
282 traversalContinuation.asContinue().getValueOrDefault(null));
Christoffer Quist Adamsene1fa9de2022-04-22 12:56:21 +0200283 if (traversalContinuation.isBreak()) {
284 break;
285 }
286 }
287 return traversalContinuation;
288 }
289
Christoffer Quist Adamsen5fbf0d52018-12-21 08:24:47 +0100290 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 Adamsen0db07672020-04-02 08:40:06 +0200298 public boolean hasUniqueSuccessor() {
299 return successors.size() == 1;
300 }
301
Christoffer Quist Adamsen575e3bf2021-02-26 11:09:55 +0100302 public boolean hasUniqueSuccessorWithUniquePredecessor() {
303 return hasUniqueSuccessor() && getUniqueSuccessor().getPredecessors().size() == 1;
304 }
305
Christoffer Quist Adamsen305a54a2020-04-03 15:34:36 +0200306 public boolean hasUniqueNormalSuccessor() {
307 return numberOfNormalSuccessors() == 1;
308 }
309
310 public boolean hasUniqueNormalSuccessorWithUniquePredecessor() {
311 return hasUniqueNormalSuccessor() && getUniqueNormalSuccessor().getPredecessors().size() == 1;
312 }
313
Christoffer Quist Adamsen0db07672020-04-02 08:40:06 +0200314 public BasicBlock getUniqueSuccessor() {
Christoffer Quist Adamsen305a54a2020-04-03 15:34:36 +0200315 assert hasUniqueSuccessor();
316 return successors.get(0);
317 }
318
319 public BasicBlock getUniqueNormalSuccessor() {
320 assert hasUniqueNormalSuccessor();
Christoffer Quist Adamsen18e2e112020-04-03 20:03:46 +0200321 return ListUtils.last(successors);
Christoffer Quist Adamsen0db07672020-04-02 08:40:06 +0200322 }
323
Mads Ager418d1ca2017-05-22 09:35:49 +0200324 public List<BasicBlock> getSuccessors() {
Andrew Grieve17df7c52023-11-10 16:38:59 -0500325 return ListUtils.unmodifiableForTesting(successors);
Christoffer Quist Adamsen6c71cd92018-12-12 09:56:03 +0100326 }
327
328 public List<BasicBlock> getMutableSuccessors() {
Christoffer Quist Adamsen5fbf0d52018-12-21 08:24:47 +0100329 assert notifySuccessorsMayChangeListeners();
Mads Ager418d1ca2017-05-22 09:35:49 +0200330 return successors;
331 }
332
Christoffer Quist Adamsen5fbf0d52018-12-21 08:24:47 +0100333 private boolean notifySuccessorsMayChangeListeners() {
334 if (onControlFlowEdgesMayChangeListeners != null) {
335 onControlFlowEdgesMayChangeListeners.forEach(l -> l.onSuccessorsMayChange(this));
336 }
337 return true;
338 }
339
Christoffer Quist Adamsen8100cf22020-04-03 13:43:31 +0200340 public void forEachNormalSuccessor(Consumer<BasicBlock> consumer) {
Christoffer Quist Adamsen18e2e112020-04-03 20:03:46 +0200341 for (int i = successors.size() - numberOfNormalSuccessors(); i < successors.size(); i++) {
Christoffer Quist Adamsen8100cf22020-04-03 13:43:31 +0200342 consumer.accept(successors.get(i));
343 }
344 }
345
346 public boolean hasNormalSuccessor(BasicBlock block) {
Christoffer Quist Adamsen18e2e112020-04-03 20:03:46 +0200347 for (int i = successors.size() - numberOfNormalSuccessors(); i < successors.size(); i++) {
Christoffer Quist Adamsen8100cf22020-04-03 13:43:31 +0200348 if (successors.get(i) == block) {
349 return true;
350 }
351 }
352 return false;
353 }
354
Christoffer Quist Adamsenab63a162023-09-06 11:16:57 +0200355 @SuppressWarnings("MixedMutabilityReturnType")
Ian Zernyad5d0a32017-10-06 11:11:20 +0200356 public List<BasicBlock> getNormalSuccessors() {
Mads Ager418d1ca2017-05-22 09:35:49 +0200357 if (!hasCatchHandlers()) {
358 return successors;
359 }
Mads Ager418d1ca2017-05-22 09:35:49 +0200360 ImmutableList.Builder<BasicBlock> normals = ImmutableList.builder();
Christoffer Quist Adamsen8100cf22020-04-03 13:43:31 +0200361 forEachNormalSuccessor(normals::add);
Mads Ager418d1ca2017-05-22 09:35:49 +0200362 return normals.build();
363 }
364
Christoffer Quist Adamsene7479ea2019-10-10 16:04:01 +0200365 public int numberOfNormalSuccessors() {
366 if (hasCatchHandlers()) {
367 return successors.size() - catchHandlers.getUniqueTargets().size();
368 }
369 return successors.size();
370 }
371
Christoffer Quist Adamsene3454452020-04-22 09:08:03 +0200372 public int numberOfExceptionalSuccessors() {
373 if (hasCatchHandlers()) {
374 return catchHandlers.getUniqueTargets().size();
375 }
376 return 0;
377 }
378
Christoffer Quist Adamsen6347ab52019-06-19 15:30:17 +0200379 public boolean hasUniquePredecessor() {
380 return predecessors.size() == 1;
381 }
382
Andrew Grieve6a196b72025-01-28 09:45:13 -0500383 public boolean hasUniquePredecessorWithUniqueSuccessor() {
384 return hasUniquePredecessor() && getUniquePredecessor().getSuccessors().size() == 1;
385 }
386
Christoffer Quist Adamsen6347ab52019-06-19 15:30:17 +0200387 public BasicBlock getUniquePredecessor() {
388 assert hasUniquePredecessor();
389 return predecessors.get(0);
390 }
391
Christoffer Adamsen31b8ed12024-08-29 16:59:58 +0200392 public BasicBlock getPredecessor(int i) {
393 return getPredecessors().get(i);
394 }
395
Mads Ager418d1ca2017-05-22 09:35:49 +0200396 public List<BasicBlock> getPredecessors() {
Andrew Grieve17df7c52023-11-10 16:38:59 -0500397 return ListUtils.unmodifiableForTesting(predecessors);
Christoffer Quist Adamsen6c71cd92018-12-12 09:56:03 +0100398 }
399
400 public List<BasicBlock> getMutablePredecessors() {
Christoffer Quist Adamsen5fbf0d52018-12-21 08:24:47 +0100401 assert notifyPredecessorsMayChangeListeners();
Mads Ager418d1ca2017-05-22 09:35:49 +0200402 return predecessors;
403 }
404
Christoffer Quist Adamsen5fbf0d52018-12-21 08:24:47 +0100405 private boolean notifyPredecessorsMayChangeListeners() {
406 if (onControlFlowEdgesMayChangeListeners != null) {
407 onControlFlowEdgesMayChangeListeners.forEach(l -> l.onPredecessorsMayChange(this));
408 }
409 return true;
410 }
411
Mads Ager418d1ca2017-05-22 09:35:49 +0200412 public List<BasicBlock> getNormalPredecessors() {
413 ImmutableList.Builder<BasicBlock> normals = ImmutableList.builder();
414 for (BasicBlock predecessor : predecessors) {
Mads Agerc52c3532017-06-14 11:54:02 +0200415 if (!predecessor.hasCatchSuccessor(this)) {
Mads Ager418d1ca2017-05-22 09:35:49 +0200416 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 Adamsen95dd66b2018-06-11 09:12:45 +0200425 removeSuccessorsByIndex(new IntArrayList(new int[] {index}));
Mads Ager418d1ca2017-05-22 09:35:49 +0200426 }
427
Christoffer Quist Adamsen4111aba2024-01-23 15:35:55 +0100428 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 Ager418d1ca2017-05-22 09:35:49 +0200441 int index = predecessors.indexOf(block);
442 assert index >= 0 : "removePredecessor did not find the predecessor to remove";
Christoffer Quist Adamsen5fbf0d52018-12-21 08:24:47 +0100443 getMutablePredecessors().remove(index);
Christoffer Quist Adamsen4111aba2024-01-23 15:35:55 +0100444 if (hasPhis()) {
Mads Ager418d1ca2017-05-22 09:35:49 +0200445 for (Phi phi : getPhis()) {
Christoffer Quist Adamsen4111aba2024-01-23 15:35:55 +0100446 phi.removeOperand(index, affectedValues, removedBlocks);
Mads Ager418d1ca2017-05-22 09:35:49 +0200447 }
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 Adamsen4111aba2024-01-23 15:35:55 +0100456 phi.removeTrivialPhi(null, affectedValues, prunedValueConsumer, removedBlocks);
Mads Ager418d1ca2017-05-22 09:35:49 +0200457 }
458 }
459 }
460
Christoffer Quist Adamsen078d6742019-06-19 15:34:13 +0200461 public void removeAllNormalSuccessors() {
462 if (hasCatchHandlers()) {
463 IntList successorsToRemove = new IntArrayList();
Andrew Grieve38e77b92024-01-19 13:53:08 -0500464
465 for (int i = numberOfExceptionalSuccessors(), l = successors.size(); i < l; i++) {
466 successorsToRemove.add(i);
Christoffer Quist Adamsen078d6742019-06-19 15:34:13 +0200467 }
468 removeSuccessorsByIndex(successorsToRemove);
469 } else {
470 successors.clear();
471 }
472 }
473
Andrew Grieve712a8ca2024-01-18 15:42:45 -0500474 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 Vnukov4dc50a32018-02-09 07:43:50 -0800485 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 Vnukovd1fd13b2018-01-22 12:08:19 -0800493 public void swapSuccessorsByIndex(int index1, int index2) {
494 assert index1 != index2;
Mads Ager418d1ca2017-05-22 09:35:49 +0200495 if (hasCatchHandlers()) {
496 List<Integer> targets = new ArrayList<>(catchHandlers.getAllTargets());
Denis Vnukovd1fd13b2018-01-22 12:08:19 -0800497 assert targets.contains(index1) == targets.contains(index2)
498 : "Swapping normal successor and catch handler";
Mads Ager418d1ca2017-05-22 09:35:49 +0200499 for (int i = 0; i < targets.size(); i++) {
Denis Vnukovd1fd13b2018-01-22 12:08:19 -0800500 if (targets.get(i) == index1) {
501 targets.set(i, index2);
502 } else if (targets.get(i) == index2) {
503 targets.set(i, index1);
Mads Ager418d1ca2017-05-22 09:35:49 +0200504 }
505 }
506 catchHandlers = new CatchHandlers<>(catchHandlers.getGuards(), targets);
507 }
Christoffer Quist Adamsen5fbf0d52018-12-21 08:24:47 +0100508 List<BasicBlock> successors = getMutableSuccessors();
Denis Vnukovd1fd13b2018-01-22 12:08:19 -0800509 BasicBlock tmp = successors.get(index1);
510 successors.set(index1, successors.get(index2));
511 successors.set(index2, tmp);
Mads Ager418d1ca2017-05-22 09:35:49 +0200512 }
513
Ian Zerny7271f712018-09-20 09:22:59 +0200514 // TODO(b/116174212): Remove the predecessor pointer from the old successor block.
Mads Ager418d1ca2017-05-22 09:35:49 +0200515 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 Vnukovd1fd13b2018-01-22 12:08:19 -0800541 swapSuccessorsByIndex(indexOfOldBlock - 1, indexOfNewBlock);
Mads Ager418d1ca2017-05-22 09:35:49 +0200542 }
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 Grievefa947352024-11-06 11:28:43 -0500546 Instruction instruction = instructions.getLast();
547 instructions.removeIgnoreValues(instruction);
Mathias Rav7a3ced22018-03-21 15:37:37 +0100548 // 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-Jespersenff0905b2020-11-03 14:19:35 +0100551 if (value.isValueOnStack()) {
552 if (!value.isPhi()
553 && (value.definition.isLoad() && value.definition.getBlock() == this)) {
Morten Krogh-Jespersen532989d2018-10-30 14:28:03 +0100554 assert hasLinearFlow(this, value.definition.getBlock());
555 value.definition.getBlock().removeInstruction(value.definition);
Mathias Rav7a3ced22018-03-21 15:37:37 +0100556 } else {
Morten Krogh-Jespersenff0905b2020-11-03 14:19:35 +0100557 assert !(value instanceof StackValues);
558 Pop pop = new Pop(value);
Mathias Rav7a3ced22018-03-21 15:37:37 +0100559 pop.setPosition(instruction.getPosition());
560 getInstructions().addLast(pop);
561 }
562 }
Mads Ager418d1ca2017-05-22 09:35:49 +0200563 if (value.hasUsersInfo()) {
564 value.removeUser(instruction);
565 }
566 }
567 Instruction exit = new Goto();
Ian Zerny0dba4dd2018-09-26 14:48:22 +0200568 exit.setPosition(instruction.getPosition());
Mads Ager418d1ca2017-05-22 09:35:49 +0200569 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 Vnukovd1fd13b2018-01-22 12:08:19 -0800573 swapSuccessorsByIndex(indexOfOldBlock - 1, indexOfNewBlock);
Mads Ager418d1ca2017-05-22 09:35:49 +0200574 }
Christoffer Quist Adamsenc17fa822019-06-20 15:43:22 +0200575 } else if (exit().isSwitch()) {
Mads Ager418d1ca2017-05-22 09:35:49 +0200576 // Rewrite fallthrough and case target indices.
Christoffer Quist Adamsenc17fa822019-06-20 15:43:22 +0200577 Switch exit = exit().asSwitch();
Mads Ager418d1ca2017-05-22 09:35:49 +0200578 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 Adamsen078d6742019-06-19 15:34:13 +0200593 } else {
594 assert exit().isReturn() || exit().isThrow();
Mads Ager418d1ca2017-05-22 09:35:49 +0200595 }
596
597 // Remove the replaced successor.
Christoffer Quist Adamsen5fbf0d52018-12-21 08:24:47 +0100598 boolean removed = getMutableSuccessors().remove(block);
Mads Ager418d1ca2017-05-22 09:35:49 +0200599 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 Adamsen5fbf0d52018-12-21 08:24:47 +0100605 getMutableSuccessors().set(i, newBlock);
Mads Ager418d1ca2017-05-22 09:35:49 +0200606 return;
607 }
608 }
609 }
610 }
611
Morten Krogh-Jespersen532989d2018-10-30 14:28:03 +0100612 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 Ager418d1ca2017-05-22 09:35:49 +0200626 public void replacePredecessor(BasicBlock block, BasicBlock newBlock) {
627 for (int i = 0; i < predecessors.size(); i++) {
628 if (predecessors.get(i) == block) {
Christoffer Quist Adamsen5fbf0d52018-12-21 08:24:47 +0100629 assert notifyPredecessorsMayChangeListeners();
630 getMutablePredecessors().set(i, newBlock);
Mads Ager418d1ca2017-05-22 09:35:49 +0200631 return;
632 }
633 }
634 assert false : "replaceSuccessor did not find the predecessor to replace";
635 }
636
Christoffer Quist Adamsen95dd66b2018-06-11 09:12:45 +0200637 public void removeSuccessorsByIndex(IntList successorsToRemove) {
Mads Ager418d1ca2017-05-22 09:35:49 +0200638 if (successorsToRemove.isEmpty()) {
639 return;
640 }
Denis Vnukovd1fd13b2018-01-22 12:08:19 -0800641 assert ListUtils.verifyListIsOrdered(successorsToRemove);
Christoffer Quist Adamsen5fbf0d52018-12-21 08:24:47 +0100642 List<BasicBlock> successors = getMutableSuccessors();
Mads Ager418d1ca2017-05-22 09:35:49 +0200643 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 Vnukovd1fd13b2018-01-22 12:08:19 -0800653 List<Integer> currentTargets = catchHandlers.getAllTargets();
654 List<DexType> currentGuards = catchHandlers.getGuards();
Mads Ager418d1ca2017-05-22 09:35:49 +0200655 int size = catchHandlers.size();
Denis Vnukovd1fd13b2018-01-22 12:08:19 -0800656 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 Ager418d1ca2017-05-22 09:35:49 +0200674 }
Denis Vnukovd1fd13b2018-01-22 12:08:19 -0800675 newTargets.add(index - decreaseBy);
676 newGuards.add(currentGuards.get(i));
Mads Ager418d1ca2017-05-22 09:35:49 +0200677 }
Denis Vnukovd1fd13b2018-01-22 12:08:19 -0800678
679 if (newTargets.isEmpty()) {
Mads Ager418d1ca2017-05-22 09:35:49 +0200680 catchHandlers = CatchHandlers.EMPTY_INDICES;
681 } else {
Denis Vnukovd1fd13b2018-01-22 12:08:19 -0800682 catchHandlers = new CatchHandlers<>(newGuards, newTargets);
Mads Ager418d1ca2017-05-22 09:35:49 +0200683 }
684 }
685 }
686
687 public void removePredecessorsByIndex(List<Integer> predecessorsToRemove) {
688 if (predecessorsToRemove.isEmpty()) {
689 return;
690 }
Christoffer Quist Adamsen5fbf0d52018-12-21 08:24:47 +0100691 List<BasicBlock> predecessors = getMutablePredecessors();
Mads Ager418d1ca2017-05-22 09:35:49 +0200692 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 Adamsen207a9f52024-12-19 11:57:04 +0100708 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 Adamsen179f7002019-07-25 08:43:47 +0200719 public boolean hasPhis() {
Christoffer Quist Adamsen4111aba2024-01-23 15:35:55 +0100720 return phis != null && !phis.isEmpty();
Christoffer Quist Adamsen179f7002019-07-25 08:43:47 +0200721 }
722
Mads Ager418d1ca2017-05-22 09:35:49 +0200723 public List<Phi> getPhis() {
724 return phis;
725 }
726
Christoffer Quist Adamsen01e790d2020-03-25 12:51:28 +0100727 public boolean isEntry() {
728 return getPredecessors().isEmpty();
729 }
730
Mads Ager418d1ca2017-05-22 09:35:49 +0200731 public boolean isFilled() {
732 return filled;
733 }
734
Christoffer Quist Adamsen85bcf9e2021-05-21 13:38:35 +0200735 public void setFilled() {
736 filled = true;
737 }
738
Mads Agerd67ce0a2017-12-22 11:37:01 +0100739 public void setFilledForTesting() {
Mads Ager418d1ca2017-05-22 09:35:49 +0200740 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 Rave9058502018-05-15 10:13:49 +0200758 public String getNumberAsString() {
759 return number >= 0 ? "" + number : "<unknown>";
760 }
761
Christoffer Adamsen383bcd82018-04-26 14:23:31 +0200762 public int numberInstructions(int nextInstructionNumber) {
763 for (Instruction instruction : instructions) {
764 instruction.setNumber(nextInstructionNumber);
Søren Gjesse36acb522019-11-27 12:27:36 +0100765 nextInstructionNumber += INSTRUCTION_NUMBER_DELTA;
Christoffer Adamsen383bcd82018-04-26 14:23:31 +0200766 }
767 return nextInstructionNumber;
768 }
769
Andrew Grievefa947352024-11-06 11:28:43 -0500770 public InstructionList getInstructions() {
Mads Ager418d1ca2017-05-22 09:35:49 +0200771 return instructions;
772 }
773
Christoffer Quist Adamsenc406cdd2022-07-01 13:17:44 +0200774 public <T extends Instruction> Iterable<T> getInstructions(Predicate<Instruction> predicate) {
775 return IterableUtils.filter(getInstructions(), predicate);
776 }
777
Christoffer Quist Adamsen49292642019-08-01 15:52:06 +0200778 public Iterable<Instruction> instructionsAfter(Instruction instruction) {
779 return () -> iterator(instruction);
780 }
781
Christoffer Quist Adamsen684e2812019-10-07 13:49:05 +0200782 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 Jeonc30c9e22018-02-06 10:36:19 -0800816 public boolean isEmpty() {
817 return instructions.isEmpty();
818 }
819
Christoffer Quist Adamsen33d22a22020-02-13 11:11:52 +0100820 public int size() {
821 return instructions.size();
822 }
823
Christoffer Quist Adamsene0f8fa62020-01-23 09:39:06 +0100824 public boolean isReturnBlock() {
825 return exit().isReturn();
826 }
827
Mads Ager418d1ca2017-05-22 09:35:49 +0200828 public Instruction entry() {
Andrew Grievefa947352024-11-06 11:28:43 -0500829 return instructions.getFirst();
Mads Ager418d1ca2017-05-22 09:35:49 +0200830 }
831
832 public JumpInstruction exit() {
833 assert filled;
Andrew Grievefa947352024-11-06 11:28:43 -0500834 assert instructions.getLast().isJumpInstruction();
835 return instructions.getLast().asJumpInstruction();
836 }
837
838 public Instruction getLastInstruction() {
839 return instructions.getLast();
Mads Ager418d1ca2017-05-22 09:35:49 +0200840 }
841
Ian Zerny603d50d2017-08-02 12:38:23 +0200842 public Instruction exceptionalExit() {
843 assert hasCatchHandlers();
Andrew Grievefa947352024-11-06 11:28:43 -0500844 for (Instruction ins = getLastInstruction(); ins != null; ins = ins.getPrev()) {
845 if (ins.instructionTypeCanThrow()) {
846 return ins;
Ian Zerny603d50d2017-08-02 12:38:23 +0200847 }
848 }
Søren Gjesse1848f402018-07-11 12:57:01 +0200849 return null;
Ian Zerny603d50d2017-08-02 12:38:23 +0200850 }
851
Mads Ager418d1ca2017-05-22 09:35:49 +0200852 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 Gjesse6a0a6552018-01-19 13:35:35 +0100863 public void mark(int color) {
864 assert color != 0;
865 assert !isMarked(color);
866 this.color |= color;
867 assert isMarked(color);
Mads Ager418d1ca2017-05-22 09:35:49 +0200868 }
869
Søren Gjesse6a0a6552018-01-19 13:35:35 +0100870 public void clearMark(int color) {
871 assert color != 0;
872 this.color &= ~color;
873 assert !isMarked(color);
Mads Ager418d1ca2017-05-22 09:35:49 +0200874 }
875
Søren Gjesse6a0a6552018-01-19 13:35:35 +0100876 public boolean isMarked(int color) {
877 assert color != 0;
878 return (this.color & color) != 0;
Mads Ager418d1ca2017-05-22 09:35:49 +0200879 }
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 Adamsen4111aba2024-01-23 15:35:55 +0100907 removePhi(phi, null, emptyConsumer());
908 }
909
910 public void removePhi(
911 Phi phi, AffectedValues affectedValues, Consumer<Value> prunedValueConsumer) {
Mads Ager418d1ca2017-05-22 09:35:49 +0200912 phis.remove(phi);
Mathias Rav1298d072018-04-16 16:08:05 +0200913 assert currentDefinitions == null || !currentDefinitions.containsValue(phi)
914 : "Attempt to remove Phi " + phi + " which is present in currentDefinitions";
Christoffer Quist Adamsen4111aba2024-01-23 15:35:55 +0100915 if (affectedValues != null) {
916 affectedValues.remove(phi);
917 }
918 prunedValueConsumer.accept(phi);
Mads Ager418d1ca2017-05-22 09:35:49 +0200919 }
920
Christoffer Quist Adamsen822ac822023-08-21 09:45:51 +0200921 public void removePhis(Collection<Phi> phisToRemove) {
922 assert currentDefinitions == null || currentDefinitions.isEmpty();
923 phis.removeAll(phisToRemove);
924 }
925
Andrew Grievefa947352024-11-06 11:28:43 -0500926 public void add(Instruction next, IRMetadata unused_metadata) {
Mads Ager418d1ca2017-05-22 09:35:49 +0200927 assert !isFilled();
Andrew Grievefa947352024-11-06 11:28:43 -0500928 instructions.addLast(next);
Mads Ager418d1ca2017-05-22 09:35:49 +0200929 }
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 Adamsen5fbf0d52018-12-21 08:24:47 +0100946 getMutableSuccessors().add(successor);
947 successor.getMutablePredecessors().add(this);
Mads Ager418d1ca2017-05-22 09:35:49 +0200948 }
949
Christoffer Quist Adamsen4111aba2024-01-23 15:35:55 +0100950 private static boolean blocksClean(Collection<BasicBlock> blocks) {
Christoffer Quist Adamsen5fbf0d52018-12-21 08:24:47 +0100951 blocks.forEach(
952 b -> {
Christoffer Quist Adamsen4111aba2024-01-23 15:35:55 +0100953 assert b.predecessors.isEmpty();
954 assert b.successors.isEmpty();
Christoffer Quist Adamsen5fbf0d52018-12-21 08:24:47 +0100955 });
Mads Ager418d1ca2017-05-22 09:35:49 +0200956 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 Adamsen5fbf0d52018-12-21 08:24:47 +0100968 unlinkedBlock.getMutableSuccessors().clear();
969 getMutablePredecessors().clear();
Mads Ager418d1ca2017-05-22 09:35:49 +0200970 return unlinkedBlock;
971 }
972
Tamas Kenez82aac822018-04-24 12:01:01 +0200973 /** 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 Adamsen5fbf0d52018-12-21 08:24:47 +0100977 List<BasicBlock> predecessors = getMutablePredecessors();
978 predecessors.get(0).getMutableSuccessors().remove(this);
979 getMutablePredecessors().clear();
Tamas Kenez82aac822018-04-24 12:01:01 +0200980 }
981
Mads Ager418d1ca2017-05-22 09:35:49 +0200982 /**
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 Adamsen5fbf0d52018-12-21 08:24:47 +0100992 unlinkedBlock.getMutablePredecessors().clear();
993 getMutableSuccessors().clear();
Mads Ager418d1ca2017-05-22 09:35:49 +0200994 return unlinkedBlock;
995 }
996
997 /**
Mads Ager4f1ae512018-04-05 11:16:59 +0200998 * Unlinks the current block based on the assumption that it is a catch handler.
Mads Ager418d1ca2017-05-22 09:35:49 +0200999 *
Mads Ager4f1ae512018-04-05 11:16:59 +02001000 * 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 Ager418d1ca2017-05-22 09:35:49 +02001002 */
Mads Ager4f1ae512018-04-05 11:16:59 +02001003 public void unlinkCatchHandler() {
1004 assert predecessors.size() == 1;
1005 predecessors.get(0).removeSuccessor(this);
Christoffer Quist Adamsen5fbf0d52018-12-21 08:24:47 +01001006 getMutablePredecessors().clear();
Mads Ager418d1ca2017-05-22 09:35:49 +02001007 }
1008
Christoffer Quist Adamsenfd7c4d52018-11-05 17:51:55 +01001009 /**
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 Adamsen7411e732018-11-06 13:14:29 +01001031 int guardIndex = catchHandlers.getGuards().indexOf(guard);
1032 if (guardIndex >= 0) {
1033 int successorIndex = catchHandlers.getAllTargets().get(guardIndex);
1034 assert successorIndex >= 0;
Christoffer Quist Adamsenfd7c4d52018-11-05 17:51:55 +01001035 catchHandlers = catchHandlers.removeGuard(guard);
1036 if (getCatchHandlers().getAllTargets().stream()
1037 .noneMatch(target -> target == successors.get(successorIndex))) {
Christoffer Quist Adamsen5fbf0d52018-12-21 08:24:47 +01001038 getMutableSuccessors().remove(successorIndex);
Christoffer Quist Adamsenfd7c4d52018-11-05 17:51:55 +01001039 }
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 Gjesse3cd05ab2017-08-22 10:26:39 +02001057 public void detachAllSuccessors() {
1058 for (BasicBlock successor : successors) {
Christoffer Quist Adamsen5fbf0d52018-12-21 08:24:47 +01001059 successor.getMutablePredecessors().remove(this);
Søren Gjesse3cd05ab2017-08-22 10:26:39 +02001060 }
Christoffer Quist Adamsen5fbf0d52018-12-21 08:24:47 +01001061 getMutableSuccessors().clear();
Søren Gjesse3cd05ab2017-08-22 10:26:39 +02001062 }
1063
Christoffer Quist Adamsen4111aba2024-01-23 15:35:55 +01001064 public Set<BasicBlock> unlink(
1065 BasicBlock successor, DominatorTree dominator, AffectedValues affectedValues) {
Morten Krogh-Jespersen969699f2019-07-09 12:14:59 +02001066 assert affectedValues != null;
Mads Ager418d1ca2017-05-22 09:35:49 +02001067 assert successors.contains(successor);
Tamas Kenez82aac822018-04-24 12:01:01 +02001068 assert successor.predecessors.size() == 1; // There are no critical edges.
1069 assert successor.predecessors.get(0) == this;
Christoffer Quist Adamsen4111aba2024-01-23 15:35:55 +01001070 Set<BasicBlock> dominatedBlocks =
1071 dominator.dominatedBlocks(successor, Sets.newIdentityHashSet());
1072 for (BasicBlock dominatedBlock : dominatedBlocks) {
1073 dominatedBlock.cleanForRemoval(affectedValues, emptyConsumer(), dominatedBlocks::contains);
Mads Ager418d1ca2017-05-22 09:35:49 +02001074 }
Christoffer Quist Adamsen4111aba2024-01-23 15:35:55 +01001075 assert blocksClean(dominatedBlocks);
1076 return dominatedBlocks;
Mads Ager418d1ca2017-05-22 09:35:49 +02001077 }
1078
Christoffer Quist Adamsen4111aba2024-01-23 15:35:55 +01001079 public void cleanForRemoval(
1080 AffectedValues affectedValues,
1081 Consumer<Value> prunedValueConsumer,
1082 Predicate<BasicBlock> removedBlocks) {
Mads Ager4f1ae512018-04-05 11:16:59 +02001083 for (BasicBlock block : successors) {
Christoffer Quist Adamsen4111aba2024-01-23 15:35:55 +01001084 block.removePredecessor(this, affectedValues, prunedValueConsumer, removedBlocks);
Mads Ager4f1ae512018-04-05 11:16:59 +02001085 }
Christoffer Quist Adamsen5fbf0d52018-12-21 08:24:47 +01001086 getMutableSuccessors().clear();
Mads Ager4f1ae512018-04-05 11:16:59 +02001087 for (BasicBlock block : predecessors) {
1088 block.removeSuccessor(this);
1089 }
Christoffer Quist Adamsen5fbf0d52018-12-21 08:24:47 +01001090 getMutablePredecessors().clear();
Mads Ager4f1ae512018-04-05 11:16:59 +02001091 for (Phi phi : getPhis()) {
Christoffer Quist Adamsen4111aba2024-01-23 15:35:55 +01001092 affectedValues.addLiveAffectedValuesOf(phi, removedBlocks);
Mads Ager4f1ae512018-04-05 11:16:59 +02001093 for (Value operand : phi.getOperands()) {
1094 operand.removePhiUser(phi);
1095 }
Christoffer Quist Adamsen4111aba2024-01-23 15:35:55 +01001096 affectedValues.remove(phi);
Christoffer Quist Adamsenb55c98e2024-01-22 15:26:00 +01001097 prunedValueConsumer.accept(phi);
Mads Ager4f1ae512018-04-05 11:16:59 +02001098 }
1099 getPhis().clear();
1100 for (Instruction instruction : getInstructions()) {
Christoffer Quist Adamsenb55c98e2024-01-22 15:26:00 +01001101 if (instruction.hasOutValue()) {
Christoffer Quist Adamsen4111aba2024-01-23 15:35:55 +01001102 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 Ager4f1ae512018-04-05 11:16:59 +02001108 }
Christoffer Quist Adamsen4111aba2024-01-23 15:35:55 +01001109 for (Value value : instruction.inValues()) {
Mads Ager4f1ae512018-04-05 11:16:59 +02001110 value.removeUser(instruction);
1111 }
1112 for (Value value : instruction.getDebugValues()) {
1113 value.removeDebugUser(instruction);
1114 }
1115 }
1116 }
1117
Mads Ager418d1ca2017-05-22 09:35:49 +02001118 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 Adamsene7479ea2019-10-10 16:04:01 +02001131 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 Adamsend4090752019-10-10 16:45:26 +02001149 targetIndex = numberOfSuccessors - numberOfNormalSuccessors;
1150 successors.add(targetIndex, target);
Christoffer Quist Adamsene7479ea2019-10-10 16:04:01 +02001151 } 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 Agerb2a18942018-08-23 13:37:02 +02001165 }
1166
Christoffer Quist Adamsen29441a12018-11-05 15:29:33 +01001167 /**
1168 * Due to class merging, it is possible that two exception classes have been merged into one. This
Ian Zernyd37ed282020-08-06 13:21:04 +02001169 * function renames the guards according to the given graph lens.
Christoffer Quist Adamsen29441a12018-11-05 15:29:33 +01001170 *
1171 * @return true if any guards were renamed.
1172 */
Christoffer Quist Adamsend139da02023-09-05 19:05:13 +02001173 @SuppressWarnings("ReferenceEquality")
Christoffer Quist Adamsen7ede3422022-01-21 14:47:03 +01001174 public boolean renameGuardsInCatchHandlers(NonIdentityGraphLens graphLens, GraphLens codeLens) {
Christoffer Quist Adamsen95dd66b2018-06-11 09:12:45 +02001175 assert hasCatchHandlers();
Christoffer Quist Adamsen29441a12018-11-05 15:29:33 +01001176 boolean anyGuardsRenamed = false;
Christoffer Quist Adamsen95dd66b2018-06-11 09:12:45 +02001177 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 Adamsen7ede3422022-01-21 14:47:03 +01001180 DexType renamed = graphLens.lookupType(guard, codeLens);
Christoffer Quist Adamsen29441a12018-11-05 15:29:33 +01001181 newGuards.add(renamed);
1182 anyGuardsRenamed |= renamed != guard;
Christoffer Quist Adamsen95dd66b2018-06-11 09:12:45 +02001183 }
Christoffer Quist Adamsen29441a12018-11-05 15:29:33 +01001184 if (anyGuardsRenamed) {
1185 this.catchHandlers = new CatchHandlers<>(newGuards, catchHandlers.getAllTargets());
1186 }
1187 return anyGuardsRenamed;
Christoffer Quist Adamsen95dd66b2018-06-11 09:12:45 +02001188 }
1189
1190 public boolean consistentCatchHandlers() {
1191 // Check that catch handlers are always the first successors of a block.
1192 if (hasCatchHandlers()) {
Christoffer Adamsen0c68ba12024-11-22 14:21:30 +01001193 assert exit().isGoto() || exit().isReturn() || exit().isThrow();
Christoffer Quist Adamsen95dd66b2018-06-11 09:12:45 +02001194 CatchHandlers<Integer> catchHandlers = getCatchHandlersWithSuccessorIndexes();
Søren Gjesse315c0532018-10-01 15:36:11 +02001195 // Check that guards are unique.
1196 assert catchHandlers.getGuards().size()
1197 == ImmutableSet.copyOf(catchHandlers.getGuards()).size();
Christoffer Quist Adamsen95dd66b2018-06-11 09:12:45 +02001198 // 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 Zernyc818a922019-03-04 10:38:55 +01001202 assert !guards.get(i).toDescriptorString().equals(DexItemFactory.throwableDescriptorString)
1203 || i == lastGuardIndex;
Christoffer Quist Adamsen95dd66b2018-06-11 09:12:45 +02001204 }
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 Grieve24f16ea2024-01-23 09:36:03 -05001220 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 Grieve712a8ca2024-01-18 15:42:45 -05001226 }
1227
Andrew Grieve24f16ea2024-01-23 09:36:03 -05001228 /**
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 Grieve712a8ca2024-01-18 15:42:45 -05001234 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 Grieve24f16ea2024-01-23 09:36:03 -05001240
Andrew Grieve712a8ca2024-01-18 15:42:45 -05001241 int numHandlers = targets1.size();
1242 if (numHandlers != targets2.size()) {
1243 return false;
1244 }
Andrew Grieve24f16ea2024-01-23 09:36:03 -05001245 if (numHandlers == 0) {
1246 return true;
1247 }
1248
1249 List<DexType> guards1 = catchHandlers.getGuards();
1250 List<DexType> guards2 = other.catchHandlers.getGuards();
Andrew Grieve712a8ca2024-01-18 15:42:45 -05001251 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 Grieve24f16ea2024-01-23 09:36:03 -05001254 BasicBlock trampolineTarget = catchBlock1.getExceptionTrampolineTarget();
1255 if (trampolineTarget == null
1256 || trampolineTarget != catchBlock2.getExceptionTrampolineTarget()
1257 || guards1.get(i).isNotIdenticalTo(guards2.get(i))) {
Andrew Grieve712a8ca2024-01-18 15:42:45 -05001258 return false;
1259 }
1260 }
1261 return true;
1262 }
1263
Mads Ager418d1ca2017-05-22 09:35:49 +02001264 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 Peltier28812452017-07-27 18:00:43 +02001274 private static int onThrowValueRegister(int register) {
Mads Ager418d1ca2017-05-22 09:35:49 +02001275 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 Zerny658e86e2017-09-08 11:16:52 +02001299 public void replaceCurrentDefinitions(Value oldValue, Value newValue) {
1300 assert oldValue.definition.getBlock() == this;
1301 assert !oldValue.isUsed();
Mathias Rav1298d072018-04-16 16:08:05 +02001302 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 Zerny658e86e2017-09-08 11:16:52 +02001313 }
1314
Mads Ager418d1ca2017-05-22 09:35:49 +02001315 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 Agerea9abac2017-06-02 14:44:20 +02001355 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 Ager418d1ca2017-05-22 09:35:49 +02001361 }
1362 sealed = true;
1363 incompletePhis.clear();
1364 }
1365 }
1366
1367 public EdgeType getEdgeType(BasicBlock successor) {
1368 assert successors.indexOf(successor) >= 0;
Mads Agerc52c3532017-06-14 11:54:02 +02001369 return hasCatchSuccessor(successor) ? EdgeType.EXCEPTIONAL : EdgeType.NORMAL;
Mads Ager418d1ca2017-05-22 09:35:49 +02001370 }
1371
Mads Agerc52c3532017-06-14 11:54:02 +02001372 public boolean hasCatchSuccessor(BasicBlock block) {
Andrew Grieve38e77b92024-01-19 13:53:08 -05001373 int numberOfExceptionalSuccessors = numberOfExceptionalSuccessors();
1374 if (numberOfExceptionalSuccessors == 0) {
Mads Ager418d1ca2017-05-22 09:35:49 +02001375 return false;
1376 }
Andrew Grieve38e77b92024-01-19 13:53:08 -05001377 int blockIndex = successors.indexOf(block);
1378 return blockIndex >= 0 && blockIndex < numberOfExceptionalSuccessors;
Mads Ager418d1ca2017-05-22 09:35:49 +02001379 }
1380
1381 public int guardsForCatchSuccessor(BasicBlock block) {
Mads Agerc52c3532017-06-14 11:54:02 +02001382 assert hasCatchSuccessor(block);
Mads Ager418d1ca2017-05-22 09:35:49 +02001383 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 Peltier28812452017-07-27 18:00:43 +02001414 private static void appendBasicBlockList(
Mads Ager418d1ca2017-05-22 09:35:49 +02001415 StringBuilder builder, List<BasicBlock> list, Function<BasicBlock, String> postfix) {
1416 if (list.size() > 0) {
1417 for (BasicBlock block : list) {
Mathias Rave9058502018-05-15 10:13:49 +02001418 builder.append(block.getNumberAsString());
Mads Ager418d1ca2017-05-22 09:35:49 +02001419 builder.append(postfix.apply(block));
Mikaël Peltier28812452017-07-27 18:00:43 +02001420 builder.append(' ');
Mads Ager418d1ca2017-05-22 09:35:49 +02001421 }
1422 } else {
Mikaël Peltier28812452017-07-27 18:00:43 +02001423 builder.append('-');
Mads Ager418d1ca2017-05-22 09:35:49 +02001424 }
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 Agerc52c3532017-06-14 11:54:02 +02001437 if (hasCatchSuccessor(block)) {
Mads Ager418d1ca2017-05-22 09:35:49 +02001438 return new String(new char[guardsForCatchSuccessor(block)]).replace("\0", "*");
1439 }
1440 return "";
1441 }
1442
Ian Zerny31054bb2017-10-10 12:12:53 +02001443 private static int digits(int number) {
1444 return (int) Math.ceil(Math.log10(number + 1));
1445 }
1446
Mads Ager418d1ca2017-05-22 09:35:49 +02001447 public String toDetailedString() {
1448 StringBuilder builder = new StringBuilder();
1449 builder.append("block ");
1450 builder.append(number);
Mads Ager418d1ca2017-05-22 09:35:49 +02001451 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 Peltier28812452017-07-27 18:00:43 +02001458 builder.append('\n');
Mads Ager418d1ca2017-05-22 09:35:49 +02001459 builder.append("predecessors: ");
1460 appendBasicBlockList(builder, predecessors, b -> "");
Mikaël Peltier28812452017-07-27 18:00:43 +02001461 builder.append('\n');
Mads Ager418d1ca2017-05-22 09:35:49 +02001462 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 Peltier28812452017-07-27 18:00:43 +02001473 builder.append('\n');
Mads Ager418d1ca2017-05-22 09:35:49 +02001474 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 Peltier28812452017-07-27 18:00:43 +02001480 builder.append('\n');
Mads Ager418d1ca2017-05-22 09:35:49 +02001481 }
1482 } else {
1483 builder.append("no phis\n");
1484 }
Ian Zerny5d16b092017-07-06 10:48:15 +02001485 if (localsAtEntry != null) {
1486 builder.append("locals: ");
1487 StringUtils.append(builder, localsAtEntry.int2ReferenceEntrySet(), ", ", BraceType.NONE);
Mikaël Peltier28812452017-07-27 18:00:43 +02001488 builder.append('\n');
Ian Zerny5d16b092017-07-06 10:48:15 +02001489 }
Ian Zerny31054bb2017-10-10 12:12:53 +02001490 int lineColumn = 0;
1491 int numberColumn = 0;
Mads Ager418d1ca2017-05-22 09:35:49 +02001492 for (Instruction instruction : instructions) {
Mathias Rave9058502018-05-15 10:13:49 +02001493 lineColumn = Math.max(lineColumn, instruction.getPositionAsString().length());
Ian Zerny31054bb2017-10-10 12:12:53 +02001494 numberColumn = Math.max(numberColumn, digits(instruction.getNumber()));
1495 }
Mathias Rave9058502018-05-15 10:13:49 +02001496 String currentPosition = null;
Ian Zerny31054bb2017-10-10 12:12:53 +02001497 for (Instruction instruction : instructions) {
1498 if (lineColumn > 0) {
1499 String line = "";
Mathias Rave9058502018-05-15 10:13:49 +02001500 if (!instruction.getPositionAsString().equals(currentPosition)) {
1501 line = currentPosition = instruction.getPositionAsString();
Ian Zerny31054bb2017-10-10 12:12:53 +02001502 }
1503 StringUtils.appendLeftPadded(builder, line, lineColumn + 1);
1504 builder.append(": ");
1505 }
1506 StringUtils.appendLeftPadded(builder, "" + instruction.getNumber(), numberColumn + 1);
Mads Ager418d1ca2017-05-22 09:35:49 +02001507 builder.append(": ");
Ian Zerny31054bb2017-10-10 12:12:53 +02001508 builder.append(instruction.toString());
Ian Zernyd73c0b42017-09-07 07:38:42 +02001509 if (DebugLocalInfo.PRINT_LEVEL != PrintLevel.NONE) {
Ian Zerny2b1dae62020-05-28 09:44:55 +02001510 if (!instruction.getDebugValues().isEmpty()) {
1511 builder.append(" [end: ");
1512 StringUtils.append(builder, instruction.getDebugValues(), ", ", BraceType.NONE);
1513 builder.append("]");
Ian Zernyd73c0b42017-09-07 07:38:42 +02001514 }
Ian Zernyd73c0b42017-09-07 07:38:42 +02001515 }
1516 builder.append("\n");
Mads Ager418d1ca2017-05-22 09:35:49 +02001517 }
1518 return builder.toString();
1519 }
1520
Mads Ager418d1ca2017-05-22 09:35:49 +02001521 /**
Stephan Herhut2ecfe142017-06-01 14:24:01 +02001522 * Remove an instruction.
1523 */
1524 public void removeInstruction(Instruction toRemove) {
Andrew Grievefa947352024-11-06 11:28:43 -05001525 instructions.removeIgnoreValues(toRemove);
Stephan Herhut2ecfe142017-06-01 14:24:01 +02001526 }
1527
1528 /**
Mads Ager418d1ca2017-05-22 09:35:49 +02001529 * Create a new basic block with a single goto instruction.
1530 *
Ian Zerny0dba4dd2018-09-26 14:48:22 +02001531 * <p>The constructed basic block has no predecessors and has one successor which is the target
1532 * block.
Mads Ager418d1ca2017-05-22 09:35:49 +02001533 *
Mads Ager418d1ca2017-05-22 09:35:49 +02001534 * @param blockNumber the block number of the goto block
Stephan Herhut6b06bfb2017-10-26 11:29:51 +02001535 * @param target the target of the goto block
Mads Ager418d1ca2017-05-22 09:35:49 +02001536 */
Christoffer Quist Adamsenbfe352b2019-08-03 10:23:29 +02001537 public static BasicBlock createGotoBlock(
1538 int blockNumber, Position position, IRMetadata metadata, BasicBlock target) {
1539 BasicBlock block = createGotoBlock(blockNumber, position, metadata);
Christoffer Quist Adamsen6c71cd92018-12-12 09:56:03 +01001540 block.getMutableSuccessors().add(target);
Mads Ager418d1ca2017-05-22 09:35:49 +02001541 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 Adamsenbfe352b2019-08-03 10:23:29 +02001551 public static BasicBlock createGotoBlock(
1552 int blockNumber, Position position, IRMetadata metadata) {
Andrew Grievefa947352024-11-06 11:28:43 -05001553 BasicBlock block = new BasicBlock(metadata);
Christoffer Quist Adamsenbfe352b2019-08-03 10:23:29 +02001554 block.add(new Goto(), metadata);
Mads Ager418d1ca2017-05-22 09:35:49 +02001555 block.close(null);
1556 block.setNumber(blockNumber);
Ian Zerny0dba4dd2018-09-26 14:48:22 +02001557 block.entry().setPosition(position);
Mads Ager418d1ca2017-05-22 09:35:49 +02001558 return block;
1559 }
1560
Søren Gjessee3ca97d2017-06-07 11:21:37 +02001561 /**
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 Gjesse3cd05ab2017-08-22 10:26:39 +02001566 * @param blockNumber the block number of the block
1567 * @param theIf the if instruction
Søren Gjessee3ca97d2017-06-07 11:21:37 +02001568 */
Christoffer Quist Adamsenbfe352b2019-08-03 10:23:29 +02001569 public static BasicBlock createIfBlock(int blockNumber, If theIf, IRMetadata metadata) {
Andrew Grievefa947352024-11-06 11:28:43 -05001570 BasicBlock block = new BasicBlock(metadata);
Christoffer Quist Adamsenbfe352b2019-08-03 10:23:29 +02001571 block.add(theIf, metadata);
Søren Gjessee3ca97d2017-06-07 11:21:37 +02001572 block.close(null);
1573 block.setNumber(blockNumber);
1574 return block;
1575 }
1576
Søren Gjesse3cd05ab2017-08-22 10:26:39 +02001577 /**
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 Adamsen49292642019-08-01 15:52:06 +02001584 * @param instructions the instructions to place before the if instruction
Søren Gjesse3cd05ab2017-08-22 10:26:39 +02001585 */
Christoffer Quist Adamsenbfe352b2019-08-03 10:23:29 +02001586 public static BasicBlock createIfBlock(
1587 int blockNumber, If theIf, IRMetadata metadata, Instruction... instructions) {
Andrew Grievefa947352024-11-06 11:28:43 -05001588 BasicBlock block = new BasicBlock(metadata);
Christoffer Quist Adamsend3cf0ce2019-06-19 15:38:31 +02001589 for (Instruction instruction : instructions) {
Christoffer Quist Adamsenbfe352b2019-08-03 10:23:29 +02001590 block.add(instruction, metadata);
Christoffer Quist Adamsend3cf0ce2019-06-19 15:38:31 +02001591 }
Christoffer Quist Adamsenbfe352b2019-08-03 10:23:29 +02001592 block.add(theIf, metadata);
Søren Gjesse3cd05ab2017-08-22 10:26:39 +02001593 block.close(null);
1594 block.setNumber(blockNumber);
1595 return block;
1596 }
1597
Christoffer Quist Adamsenbfe352b2019-08-03 10:23:29 +02001598 public static BasicBlock createSwitchBlock(
1599 int blockNumber, IntSwitch theSwitch, IRMetadata metadata) {
Andrew Grievefa947352024-11-06 11:28:43 -05001600 BasicBlock block = new BasicBlock(metadata);
Christoffer Quist Adamsenbfe352b2019-08-03 10:23:29 +02001601 block.add(theSwitch, metadata);
Søren Gjesse3cd05ab2017-08-22 10:26:39 +02001602 block.close(null);
1603 block.setNumber(blockNumber);
1604 return block;
1605 }
1606
Jinseong Jeonb7f85322018-10-05 12:23:36 -07001607 public static BasicBlock createRethrowBlock(
Christoffer Quist Adamsen446b9312019-04-26 11:18:09 +02001608 IRCode code, Position position, DexType guard, AppView<?> appView) {
Christoffer Quist Adamsene0302c22020-03-20 14:39:56 +01001609 TypeElement guardTypeLattice =
1610 TypeElement.fromDexType(guard, Nullability.definitelyNotNull(), appView);
Andrew Grievefa947352024-11-06 11:28:43 -05001611 BasicBlock block = new BasicBlock(code.metadata());
Christoffer Quist Adamsen2e7d7e42019-03-12 15:43:07 +01001612 MoveException moveException =
Christoffer Quist Adamsen26a3f782023-10-12 10:44:53 +02001613 new MoveException(code.createValue(guardTypeLattice), guard, appView.options());
Mads Agerb2a18942018-08-23 13:37:02 +02001614 moveException.setPosition(position);
1615 Throw throwInstruction = new Throw(moveException.outValue);
1616 throwInstruction.setPosition(position);
Andrew Grievefa947352024-11-06 11:28:43 -05001617 block.instructions.addLast(moveException);
1618 block.instructions.addLast(throwInstruction);
Mads Agerb2a18942018-08-23 13:37:02 +02001619 block.close(null);
Morten Krogh-Jespersenb6ec9ed2020-08-07 12:59:26 +02001620 block.setNumber(code.getNextBlockNumber());
Mads Agerb2a18942018-08-23 13:37:02 +02001621 return block;
1622 }
1623
Christoffer Quist Adamsene77fd712021-06-18 10:57:03 +02001624 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 Ager418d1ca2017-05-22 09:35:49 +02001637 public boolean isTrivialGoto() {
1638 return instructions.size() == 1 && exit().isGoto();
1639 }
1640
Christoffer Quist Adamsen6347ab52019-06-19 15:30:17 +02001641 // 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 Zerny31054bb2017-10-10 12:12:53 +02001660 // Find the final target from this goto block. Returns null if the goto chain is cyclic.
1661 public BasicBlock endOfGotoChain() {
Denis Vnukov2801bfd2018-01-31 08:48:33 -08001662 // See Floyd's cycle-finding algorithm for reference.
Ian Zerny31054bb2017-10-10 12:12:53 +02001663 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 Kenez2f30be02018-02-28 11:11:00 +01001677 public boolean isSimpleAlwaysThrowingPath() {
Denis Vnukov2801bfd2018-01-31 08:48:33 -08001678 // 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 Vnukov2801bfd2018-01-31 08:48:33 -08001684 if (normalSuccessors.size() > 1) {
1685 return false;
1686 }
1687
Denis Vnukov4dc50a32018-02-09 07:43:50 -08001688 if (normalSuccessors.size() == 0) {
1689 return hare.exit().isThrow();
1690 }
1691
Denis Vnukov2801bfd2018-01-31 08:48:33 -08001692 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 Zerny31054bb2017-10-10 12:12:53 +02001701 public Position getPosition() {
Ian Zerny0dba4dd2018-09-26 14:48:22 +02001702 return entry().getPosition();
Ian Zerny31054bb2017-10-10 12:12:53 +02001703 }
1704
Mads Ager418d1ca2017-05-22 09:35:49 +02001705 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 Adamsena911b272018-08-17 14:35:51 +02001730 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 Ager418d1ca2017-05-22 09:35:49 +02001744 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 Grievefa947352024-11-06 11:28:43 -05001769 for (Instruction instruction = getLastInstruction();
1770 instruction != null;
1771 instruction = instruction.getPrev()) {
Mads Ager418d1ca2017-05-22 09:35:49 +02001772 if (instruction.instructionTypeCanThrow()) {
1773 return true;
1774 }
1775 assert instruction.outValue() == null;
1776 }
1777 }
1778 return true;
1779 }
1780
Andrew Grievefa947352024-11-06 11:28:43 -05001781 public InstructionIterator iterator() {
1782 return instructions.iterator();
Mads Ager418d1ca2017-05-22 09:35:49 +02001783 }
1784
Christoffer Adamsen1024c282025-03-20 11:13:12 +00001785 public BasicBlockInstructionListIterator iterator(Instruction instruction) {
Andrew Grievefa947352024-11-06 11:28:43 -05001786 return instructions.iterator(instruction);
Mads Ager418d1ca2017-05-22 09:35:49 +02001787 }
1788
Andrew Grievefa947352024-11-06 11:28:43 -05001789 public BasicBlockInstructionListIterator listIterator() {
1790 return new BasicBlockInstructionListIterator(this);
Christoffer Quist Adamsen49292642019-08-01 15:52:06 +02001791 }
1792
Andrew Grieveee125342024-11-14 12:20:39 -05001793 // TODO(b/376663044): Convert uses of index to use Instruction instead.
1794 public BasicBlockInstructionListIterator listIterator(int index) {
Andrew Grievefa947352024-11-06 11:28:43 -05001795 return new BasicBlockInstructionListIterator(this, index);
Christoffer Quist Adamsen49292642019-08-01 15:52:06 +02001796 }
1797
Andrew Grievefa947352024-11-06 11:28:43 -05001798 /** Creates an instruction list iterator starting at <code>firstInstructionToReturn</code>. */
Andrew Grieveee125342024-11-14 12:20:39 -05001799 public BasicBlockInstructionListIterator listIterator(Instruction firstInstructionToReturn) {
Andrew Grievefa947352024-11-06 11:28:43 -05001800 return new BasicBlockInstructionListIterator(this, firstInstructionToReturn);
Søren Gjessef9417d82017-07-06 14:14:41 +02001801 }
1802
1803 /**
Mads Ager418d1ca2017-05-22 09:35:49 +02001804 * Creates a new empty block as a successor for this block.
1805 *
Andrew Grievefa947352024-11-06 11:28:43 -05001806 * <p>The new block will have all the normal successors of the original block.
Mads Ager418d1ca2017-05-22 09:35:49 +02001807 *
Andrew Grievefa947352024-11-06 11:28:43 -05001808 * <p>The catch successors are either on the original block or the new block depending on the
Mads Ager418d1ca2017-05-22 09:35:49 +02001809 * value of <code>keepCatchHandlers</code>.
1810 *
Andrew Grievec18e93f2024-11-13 14:30:16 -05001811 * <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 Ager418d1ca2017-05-22 09:35:49 +02001814 *
1815 * @param blockNumber block number for new block
1816 * @param keepCatchHandlers keep catch successors on the original block
Andrew Grievefa947352024-11-06 11:28:43 -05001817 * @param firstInstructionOfNewBlock this and all following are moved to the new block
Mads Ager418d1ca2017-05-22 09:35:49 +02001818 * @return the new block
1819 */
Andrew Grievefa947352024-11-06 11:28:43 -05001820 BasicBlock createSplitBlock(
1821 int blockNumber, boolean keepCatchHandlers, Instruction firstInstructionOfNewBlock) {
Mads Ager418d1ca2017-05-22 09:35:49 +02001822 boolean hadCatchHandlers = hasCatchHandlers();
Andrew Grievefa947352024-11-06 11:28:43 -05001823 BasicBlock newBlock = new BasicBlock(metadata);
Mads Ager418d1ca2017-05-22 09:35:49 +02001824 newBlock.setNumber(blockNumber);
1825
1826 // Copy all successors including catch handlers to the new block, and update predecessors.
Christoffer Quist Adamsen5fbf0d52018-12-21 08:24:47 +01001827 newBlock.getMutableSuccessors().addAll(successors);
Mads Ager418d1ca2017-05-22 09:35:49 +02001828 for (BasicBlock successor : newBlock.getSuccessors()) {
1829 successor.replacePredecessor(this, newBlock);
1830 }
Christoffer Quist Adamsen5fbf0d52018-12-21 08:24:47 +01001831 getMutableSuccessors().clear();
Mads Ager418d1ca2017-05-22 09:35:49 +02001832 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 Grievec18e93f2024-11-13 14:30:16 -05001843 Goto newGoto = new Goto();
Andrew Grievefa947352024-11-06 11:28:43 -05001844 if (firstInstructionOfNewBlock != null) {
1845 newBlock.instructions.severFrom(firstInstructionOfNewBlock);
Andrew Grievec18e93f2024-11-13 14:30:16 -05001846 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 Grievefa947352024-11-06 11:28:43 -05001854 }
1855
Mads Ager418d1ca2017-05-22 09:35:49 +02001856 // Mark the new block filled and sealed.
1857 newBlock.filled = true;
1858 newBlock.sealed = true;
1859
1860 return newBlock;
1861 }
1862
1863 /**
Andrew Grievec18e93f2024-11-13 14:30:16 -05001864 * 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 Ager418d1ca2017-05-22 09:35:49 +02001892 * 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 Adamsen5fbf0d52018-12-21 08:24:47 +01001897 fromBlock.getMutableSuccessors().remove(successor);
Morten Krogh-Jespersen969699f2019-07-09 12:14:59 +02001898 successor.removePredecessor(fromBlock, null);
Mads Ager418d1ca2017-05-22 09:35:49 +02001899 }
1900 fromBlock.catchHandlers = CatchHandlers.EMPTY_INDICES;
1901 }
1902
1903 /**
1904 * Clone catch successors from `fromBlock` into this block.
1905 */
1906 public void copyCatchHandlers(
Mads Agerb83918c2019-01-10 12:20:45 +01001907 IRCode code,
1908 ListIterator<BasicBlock> blockIterator,
1909 BasicBlock fromBlock,
1910 InternalOptions options) {
Ian Zernyc818a922019-03-04 10:38:55 +01001911 if (catchHandlers != null && catchHandlers.hasCatchAll(options.itemFactory)) {
Mads Ager418d1ca2017-05-22 09:35:49 +02001912 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 Adamsena117f6e2020-12-23 20:01:38 +01001927 catchSuccessor.splitCriticalExceptionEdges(code, blockIterator, options);
Mads Ager418d1ca2017-05-22 09:35:49 +02001928 }
1929 }
1930
Mads Ager418d1ca2017-05-22 09:35:49 +02001931 /**
Christoffer Quist Adamsenbfe352b2019-08-03 10:23:29 +02001932 * 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 Ager418d1ca2017-05-22 09:35:49 +02001935 *
Christoffer Quist Adamsenbfe352b2019-08-03 10:23:29 +02001936 * <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 Ager418d1ca2017-05-22 09:35:49 +02001939 *
Christoffer Quist Adamsenbfe352b2019-08-03 10:23:29 +02001940 * <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 Ager418d1ca2017-05-22 09:35:49 +02001943 */
Morten Krogh-Jespersenb6ec9ed2020-08-07 12:59:26 +02001944 public void splitCriticalExceptionEdges(
Christoffer Quist Adamsena117f6e2020-12-23 20:01:38 +01001945 IRCode code, ListIterator<BasicBlock> blockIterator, InternalOptions options) {
Christoffer Quist Adamsen6c71cd92018-12-12 09:56:03 +01001946 List<BasicBlock> predecessors = getMutablePredecessors();
Mads Ager418d1ca2017-05-22 09:35:49 +02001947 boolean hasMoveException = entry().isMoveException();
Christoffer Quist Adamsene0302c22020-03-20 14:39:56 +01001948 TypeElement exceptionTypeLattice = null;
Mads Agerb83918c2019-01-10 12:20:45 +01001949 DexType exceptionType = null;
Mads Ager418d1ca2017-05-22 09:35:49 +02001950 MoveException move = null;
Ian Zerny31054bb2017-10-10 12:12:53 +02001951 Position position = entry().getPosition();
Mads Ager418d1ca2017-05-22 09:35:49 +02001952 if (hasMoveException) {
1953 // Remove the move-exception instruction.
1954 move = entry().asMoveException();
Christoffer Quist Adamsen1916c352020-03-23 11:30:48 +01001955 exceptionTypeLattice = move.getOutType();
Mads Agerb83918c2019-01-10 12:20:45 +01001956 exceptionType = move.getExceptionType();
Ian Zernyfc580802017-08-22 11:02:13 +02001957 assert move.getDebugValues().isEmpty();
Andrew Grievefa947352024-11-06 11:28:43 -05001958 instructions.removeIgnoreValues(move);
Mads Ager418d1ca2017-05-22 09:35:49 +02001959 }
1960 // Create new predecessor blocks.
Christoffer Quist Adamsena117f6e2020-12-23 20:01:38 +01001961 List<BasicBlock> newPredecessors = new ArrayList<>(predecessors.size());
Mads Ager418d1ca2017-05-22 09:35:49 +02001962 List<Value> values = new ArrayList<>(predecessors.size());
1963 for (BasicBlock predecessor : predecessors) {
Mads Agerc52c3532017-06-14 11:54:02 +02001964 if (!predecessor.hasCatchSuccessor(this)) {
1965 throw new CompilationError(
1966 "Invalid block structure: catch block reachable via non-exceptional flow.");
1967 }
Andrew Grievefa947352024-11-06 11:28:43 -05001968 BasicBlock newBlock = new BasicBlock(metadata);
Morten Krogh-Jespersenb6ec9ed2020-08-07 12:59:26 +02001969 newBlock.setNumber(code.getNextBlockNumber());
Mads Ager418d1ca2017-05-22 09:35:49 +02001970 newPredecessors.add(newBlock);
1971 if (hasMoveException) {
Christoffer Quist Adamsen26a3f782023-10-12 10:44:53 +02001972 Value value = code.createValue(exceptionTypeLattice, move.getLocalInfo());
Mads Ager418d1ca2017-05-22 09:35:49 +02001973 values.add(value);
Mads Agerb83918c2019-01-10 12:20:45 +01001974 MoveException newMove = new MoveException(value, exceptionType, options);
Andrew Grievefa947352024-11-06 11:28:43 -05001975 newBlock.instructions.addLast(newMove);
Ian Zerny31054bb2017-10-10 12:12:53 +02001976 newMove.setPosition(position);
Mads Ager418d1ca2017-05-22 09:35:49 +02001977 }
Ian Zerny0dba4dd2018-09-26 14:48:22 +02001978 Goto next = new Goto();
1979 next.setPosition(position);
Andrew Grievefa947352024-11-06 11:28:43 -05001980 newBlock.instructions.addLast(next);
Mads Ager418d1ca2017-05-22 09:35:49 +02001981 newBlock.close(null);
Christoffer Quist Adamsen6c71cd92018-12-12 09:56:03 +01001982 newBlock.getMutableSuccessors().add(this);
1983 newBlock.getMutablePredecessors().add(predecessor);
Mads Ager418d1ca2017-05-22 09:35:49 +02001984 predecessor.replaceSuccessor(this, newBlock);
Ian Zernyf33d3ca2022-12-02 13:48:43 +01001985 if (blockIterator == null) {
1986 code.blocks.add(newBlock);
1987 } else {
1988 blockIterator.add(newBlock);
1989 }
Mads Ager418d1ca2017-05-22 09:35:49 +02001990 }
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 Zernya30db872018-08-17 10:50:54 +02001996 Phi phi =
1997 new Phi(
Christoffer Quist Adamsenbfe352b2019-08-03 10:23:29 +02001998 code.valueNumberGenerator.next(),
Ian Zernya30db872018-08-17 10:50:54 +02001999 this,
Jinseong Jeonb7f85322018-10-05 12:23:36 -07002000 exceptionTypeLattice,
Ian Zernya30db872018-08-17 10:50:54 +02002001 move.getLocalInfo(),
2002 RegisterReadType.NORMAL);
Mads Ager418d1ca2017-05-22 09:35:49 +02002003 phi.addOperands(values);
2004 move.outValue().replaceUsers(phi);
2005 }
2006 }
2007
Stephan Herhut2ecfe142017-06-01 14:24:01 +02002008 /**
2009 * Append catch handlers from another block <code>fromBlock</code> (which must have catch
Mads Ager418d1ca2017-05-22 09:35:49 +02002010 * 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 Peltierbe71dbc2017-07-28 11:33:14 +02002017 * @return the catch successors that are reused in both blocks after appending.
Mads Ager418d1ca2017-05-22 09:35:49 +02002018 */
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 Gjesse315c0532018-10-01 15:36:11 +02002049 if (newCatchGuards.contains(prevCatchGuard)) {
2050 continue;
2051 }
Mads Ager418d1ca2017-05-22 09:35:49 +02002052 BasicBlock catchSuccessor = fromBlock.successors.get(prevCatchTarget);
Christoffer Quist Adamsene7479ea2019-10-10 16:04:01 +02002053 // We assume that all the catch handlers targets has only one predecessor and, thus, no phis.
Mads Ager418d1ca2017-05-22 09:35:49 +02002054 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 Adamsen5fbf0d52018-12-21 08:24:47 +01002067 List<BasicBlock> successors = getMutableSuccessors();
Mads Ager418d1ca2017-05-22 09:35:49 +02002068 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 Peltiere8b2b162017-11-08 08:52:25 +01002097
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 Gjessee96740d2019-11-26 17:13:36 +01002103 Set<BasicBlock> visitedBlocks = Sets.newIdentityHashSet();
Mikaël Peltiere8b2b162017-11-08 08:52:25 +01002104 ArrayDeque<BasicBlock> blocks = new ArrayDeque<>();
2105 blocks.push(this);
2106
Denis Vnukovd1fd13b2018-01-22 12:08:19 -08002107 while (!blocks.isEmpty()) {
Mikaël Peltiere8b2b162017-11-08 08:52:25 +01002108 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 }
mikaelpeltier99bca2f2017-12-22 14:49:17 +01002122
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 Adamsend139da02023-09-05 19:05:13 +02002145 @SuppressWarnings("ReferenceEquality")
mikaelpeltier99bca2f2017-12-22 14:49:17 +01002146 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 Zerny0ca4b642020-05-26 21:10:46 +02002154 if (replacement == null) {
mikaelpeltier99bca2f2017-12-22 14:49:17 +01002155 wrapper2phi.put(key, phi);
Ian Zerny0ca4b642020-05-26 21:10:46 +02002156 continue;
mikaelpeltier99bca2f2017-12-22 14:49:17 +01002157 }
Ian Zerny0ca4b642020-05-26 21:10:46 +02002158 // 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();
mikaelpeltier99bca2f2017-12-22 14:49:17 +01002182 }
2183 }
Morten Krogh-Jespersenbb340d12022-08-30 12:23:01 +02002184
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 Ager418d1ca2017-05-22 09:35:49 +02002199}