• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4 package com.android.tools.r8.ir.regalloc;
5 
6 import static com.android.tools.r8.dex.Constants.U16BIT_MAX;
7 import static com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator.NO_REGISTER;
8 
9 import com.android.tools.r8.dex.Constants;
10 import com.android.tools.r8.ir.code.MoveType;
11 import com.android.tools.r8.ir.code.Value;
12 import com.android.tools.r8.utils.CfgPrinter;
13 import java.util.ArrayList;
14 import java.util.Iterator;
15 import java.util.List;
16 import java.util.TreeSet;
17 
18 public class LiveIntervals {
19 
20   private final Value value;
21   private LiveIntervals nextConsecutive;
22   private LiveIntervals previousConsecutive;
23   private LiveIntervals splitParent;
24   private List<LiveIntervals> splitChildren = new ArrayList<>();
25   private List<LiveRange> ranges = new ArrayList<>();
26   private TreeSet<LiveIntervalsUse> uses = new TreeSet<>();
27   private int numberOfConsecutiveRegisters = -1;
28   private int register = NO_REGISTER;
29   private LiveIntervals hint;
30   private boolean spilled = false;
31 
32   // Only registers up to and including the registerLimit are allowed for this interval.
33   private int registerLimit = U16BIT_MAX;
34 
35   // Max register used for any of the non-spilled splits for these live intervals or for any of the
36   // live intervals that this live interval is connected to by phi moves. This is used to
37   // conservatively determine if it is safe to use rematerialization for this value.
38   private int maxNonSpilledRegister = NO_REGISTER;
39 
LiveIntervals(Value value)40   LiveIntervals(Value value) {
41     this.value = value;
42     splitParent = this;
43     value.setLiveIntervals(this);
44   }
45 
LiveIntervals(LiveIntervals splitParent)46   LiveIntervals(LiveIntervals splitParent) {
47     this.splitParent = splitParent;
48     value = splitParent.value;
49   }
50 
toInstructionPosition(int position)51   private int toInstructionPosition(int position) {
52     return position % 2 == 0 ? position : position + 1;
53   }
54 
toGapPosition(int position)55   private int toGapPosition(int position) {
56     return position % 2 == 1 ? position : position - 1;
57   }
58 
getType()59   public MoveType getType() {
60     return value.outType();
61   }
62 
getValue()63   public Value getValue() {
64     return value;
65   }
66 
requiredRegisters()67   public int requiredRegisters() {
68     return getType() == MoveType.WIDE ? 2 : 1;
69   }
70 
setHint(LiveIntervals intervals)71   public void setHint(LiveIntervals intervals) {
72     hint = intervals;
73   }
74 
getHint()75   public LiveIntervals getHint() {
76     return hint;
77   }
78 
setSpilled(boolean value)79   public void setSpilled(boolean value) {
80     spilled = value;
81   }
82 
isSpilled()83   public boolean isSpilled() {
84     return spilled;
85   }
86 
isRematerializable(LinearScanRegisterAllocator registerAllocator)87   public boolean isRematerializable(LinearScanRegisterAllocator registerAllocator) {
88     if (!value.isConstant()) {
89       return false;
90     }
91     // If one of the non-spilled splits uses a register that is higher than U8BIT_MAX we cannot
92     // rematerialize it using a ConstNumber instruction and we use spill moves instead of
93     // rematerialization. We use this check both before and after we have computed the set
94     // of unused registers. We therefore have to be careful to use the same max number for
95     // these computations. We use the unadjusted real register number to make sure that
96     // isRematerializable for the same intervals does not change from one phase of
97     // compilation to the next.
98     int max = registerAllocator.unadjustedRealRegisterFromAllocated(getMaxNonSpilledRegister());
99     return max < Constants.U8BIT_MAX;
100   }
101 
isSpilledAndRematerializable(LinearScanRegisterAllocator allocator)102   public boolean isSpilledAndRematerializable(LinearScanRegisterAllocator allocator) {
103     return isSpilled() && isRematerializable(allocator);
104   }
105 
link(LiveIntervals next)106   public void link(LiveIntervals next) {
107     assert numberOfConsecutiveRegisters == -1;
108     nextConsecutive = next;
109     next.previousConsecutive = this;
110   }
111 
isLinked()112   public boolean isLinked() {
113     return splitParent.previousConsecutive != null || splitParent.nextConsecutive != null;
114   }
115 
isArgumentInterval()116   public boolean isArgumentInterval() {
117     // TODO(ager): This is pretty indirect. We might want to have a more direct indication.
118     LiveIntervals current = splitParent;
119     while (current.previousConsecutive != null) {
120       current = current.previousConsecutive;
121     }
122     return current.ranges.get(0).isInfinite();
123   }
124 
125 
getStartOfConsecutive()126   public LiveIntervals getStartOfConsecutive() {
127     LiveIntervals current = this;
128     while (current.previousConsecutive != null) {
129       current = current.previousConsecutive;
130     }
131     return current;
132   }
133 
getNextConsecutive()134   public LiveIntervals getNextConsecutive() {
135     return nextConsecutive;
136   }
137 
getPreviousConsecutive()138   public LiveIntervals getPreviousConsecutive() {
139     return previousConsecutive;
140   }
141 
numberOfConsecutiveRegisters()142   public int numberOfConsecutiveRegisters() {
143     LiveIntervals start = getStartOfConsecutive();
144     if (start.numberOfConsecutiveRegisters != -1) {
145       assert start.numberOfConsecutiveRegisters == computeNumberOfConsecutiveRegisters();
146       return start.numberOfConsecutiveRegisters;
147     }
148     return computeNumberOfConsecutiveRegisters();
149   }
150 
computeNumberOfConsecutiveRegisters()151   private int computeNumberOfConsecutiveRegisters() {
152     LiveIntervals start = getStartOfConsecutive();
153     int result = 0;
154     for (LiveIntervals current = start;
155         current != null;
156         current = current.nextConsecutive) {
157       result += current.requiredRegisters();
158     }
159     start.numberOfConsecutiveRegisters = result;
160     return result;
161   }
162 
hasSplits()163   public boolean hasSplits() {
164     return splitChildren.size() != 0;
165   }
166 
getSplitChildren()167   public List<LiveIntervals> getSplitChildren() {
168     return splitChildren;
169   }
170 
getSplitParent()171   public LiveIntervals getSplitParent() {
172     return splitParent;
173   }
174 
175   /**
176    * Add a live range to the intervals.
177    *
178    * @param range the range to add
179    */
addRange(LiveRange range)180   public void addRange(LiveRange range) {
181     boolean added = tryAddRange(range);
182     assert added;
183   }
184 
tryAddRange(LiveRange range)185   private boolean tryAddRange(LiveRange range) {
186     if (ranges.size() > 0) {
187       LiveRange lastRange = ranges.get(ranges.size() - 1);
188       if (lastRange.isInfinite()) {
189         return false;
190       }
191       int rangeStartInstructionPosition = toInstructionPosition(range.start);
192       int lastRangeEndInstructionPosition = toInstructionPosition(lastRange.end);
193       if (lastRangeEndInstructionPosition > rangeStartInstructionPosition) {
194         return false;
195       }
196       if (lastRangeEndInstructionPosition == rangeStartInstructionPosition) {
197         lastRange.end = range.end;
198         return true;
199       }
200     }
201     ranges.add(range);
202     return true;
203   }
204 
205   /**
206    * Record a use for this interval.
207    */
addUse(LiveIntervalsUse use)208   public void addUse(LiveIntervalsUse use) {
209     uses.add(use);
210     updateRegisterConstraint(use.getLimit());
211   }
212 
updateRegisterConstraint(int constraint)213   public void updateRegisterConstraint(int constraint) {
214     registerLimit = Math.min(registerLimit, constraint);
215   }
216 
getUses()217   public TreeSet<LiveIntervalsUse> getUses() {
218     return uses;
219   }
220 
getRanges()221   public List<LiveRange> getRanges() {
222     return ranges;
223   }
224 
getStart()225   public int getStart() {
226     assert !ranges.isEmpty();
227     return ranges.get(0).start;
228   }
229 
getEnd()230   public int getEnd() {
231     assert !ranges.isEmpty();
232     return ranges.get(ranges.size() - 1).end;
233   }
234 
getRegister()235   public int getRegister() {
236     return register;
237   }
238 
getRegisterLimit()239   public int getRegisterLimit() {
240     return registerLimit;
241   }
242 
setRegister(int n)243   public void setRegister(int n) {
244     assert register == NO_REGISTER || register == n;
245     register = n;
246   }
247 
computeMaxNonSpilledRegister()248   private int computeMaxNonSpilledRegister() {
249     assert splitParent == this;
250     assert maxNonSpilledRegister == NO_REGISTER;
251     if (!isSpilled()) {
252       maxNonSpilledRegister = getRegister();
253     }
254     for (LiveIntervals child : splitChildren) {
255       if (!child.isSpilled()) {
256         maxNonSpilledRegister = Math.max(maxNonSpilledRegister, child.getRegister());
257       }
258     }
259     return maxNonSpilledRegister;
260   }
261 
setMaxNonSpilledRegister(int i)262   public void setMaxNonSpilledRegister(int i) {
263     assert i >= splitParent.maxNonSpilledRegister;
264     splitParent.maxNonSpilledRegister = i;
265   }
266 
getMaxNonSpilledRegister()267   public int getMaxNonSpilledRegister() {
268     if (splitParent.maxNonSpilledRegister != NO_REGISTER) {
269       return splitParent.maxNonSpilledRegister;
270     }
271     return splitParent.computeMaxNonSpilledRegister();
272   }
273 
usesRegister(int n)274   public boolean usesRegister(int n) {
275     if (register == n || (getType() == MoveType.WIDE && register + 1 == n)) {
276       return true;
277     }
278     return false;
279   }
280 
clearRegisterAssignment()281   public void clearRegisterAssignment() {
282     register = NO_REGISTER;
283     hint = null;
284   }
285 
overlapsPosition(int position)286   public boolean overlapsPosition(int position) {
287     for (LiveRange range : ranges) {
288       if (range.start > position) {
289         // Ranges are sorted. When a range starts after position there is no overlap.
290         return false;
291       }
292       if (position < range.end) {
293         return true;
294       }
295     }
296     return false;
297   }
298 
overlaps(LiveIntervals other)299   public boolean overlaps(LiveIntervals other) {
300     return nextOverlap(other) != -1;
301   }
302 
nextOverlap(LiveIntervals other)303   public int nextOverlap(LiveIntervals other) {
304     Iterator<LiveRange> it = other.ranges.iterator();
305     LiveRange otherRange = it.next();
306     for (LiveRange range : ranges) {
307       while (otherRange.end <= range.start) {
308         if (!it.hasNext()) {
309           return -1;
310         }
311         otherRange = it.next();
312       }
313       if (otherRange.start < range.end) {
314         return otherRange.start;
315       }
316     }
317     return -1;
318   }
319 
firstUseAfter(int unhandledStart)320   public int firstUseAfter(int unhandledStart) {
321     for (LiveIntervalsUse use : uses) {
322       if (use.getPosition() >= unhandledStart) {
323         return use.getPosition();
324       }
325     }
326     return Integer.MAX_VALUE;
327   }
328 
getFirstUse()329   public int getFirstUse() {
330     return uses.first().getPosition();
331   }
332 
firstUseWithConstraint()333   public LiveIntervalsUse firstUseWithConstraint() {
334     for (LiveIntervalsUse use : uses) {
335       if (use.hasConstraint()) {
336         return use;
337       }
338     }
339     return null;
340   }
341 
splitBefore(int start)342   public LiveIntervals splitBefore(int start) {
343     if (toInstructionPosition(start) == toInstructionPosition(getStart())) {
344       assert uses.size() == 0 || getFirstUse() != start;
345       register = NO_REGISTER;
346       return this;
347     }
348     start = toGapPosition(start);
349     LiveIntervals splitChild = new LiveIntervals(splitParent);
350     splitParent.splitChildren.add(splitChild);
351     List<LiveRange> beforeSplit = new ArrayList<>();
352     List<LiveRange> afterSplit = new ArrayList<>();
353     if (start == getEnd()) {
354       beforeSplit = ranges;
355       afterSplit.add(new LiveRange(start, start));
356     } else {
357       int rangeToSplitIndex = 0;
358       for (; rangeToSplitIndex < ranges.size(); rangeToSplitIndex++) {
359         LiveRange range = ranges.get(rangeToSplitIndex);
360         if (range.start <= start && range.end > start) {
361           break;
362         }
363         if (range.start > start) {
364           break;
365         }
366       }
367       LiveRange rangeToSplit = ranges.get(rangeToSplitIndex);
368       beforeSplit.addAll(ranges.subList(0, rangeToSplitIndex));
369       if (rangeToSplit.start < start) {
370         beforeSplit.add(new LiveRange(rangeToSplit.start, start));
371         afterSplit.add(new LiveRange(start, rangeToSplit.end));
372       } else {
373         afterSplit.add(rangeToSplit);
374       }
375       afterSplit.addAll(ranges.subList(rangeToSplitIndex + 1, ranges.size()));
376     }
377     splitChild.ranges = afterSplit;
378     ranges = beforeSplit;
379     while (!uses.isEmpty() && uses.last().getPosition() >= start) {
380       splitChild.addUse(uses.pollLast());
381     }
382     // Recompute limit after having removed uses from this interval.
383     recomputeLimit();
384     assert !ranges.isEmpty();
385     assert !splitChild.ranges.isEmpty();
386     return splitChild;
387   }
388 
recomputeLimit()389   private void recomputeLimit() {
390     registerLimit = U16BIT_MAX;
391     for (LiveIntervalsUse use : uses) {
392       updateRegisterConstraint(use.getLimit());
393     }
394   }
395 
getSplitCovering(int instructionNumber)396   public LiveIntervals getSplitCovering(int instructionNumber) {
397     assert getSplitParent() == this;
398     // Check if this interval itself is covering the instruction.
399     if (getStart() <= instructionNumber && getEnd() > instructionNumber) {
400       return this;
401     }
402     // If the instruction number is not in this intervals range, we go through all split children.
403     // If we do not find a child that contains the instruction number we return the interval
404     // whose end is the instruction number. This is needed when transitioning values across
405     // control-flow boundaries.
406     LiveIntervals matchingEnd = getEnd() == instructionNumber ? this : null;
407     for (LiveIntervals splitChild : splitChildren) {
408       if (splitChild.getStart() <= instructionNumber && splitChild.getEnd() > instructionNumber) {
409         return splitChild;
410       }
411       if (splitChild.getEnd() == instructionNumber) {
412         matchingEnd = splitChild;
413       }
414     }
415     if (matchingEnd != null) {
416       return matchingEnd;
417     }
418     assert false : "Couldn't find split covering instruction position.";
419     return null;
420   }
421 
isConstantNumberInterval()422   public boolean isConstantNumberInterval() {
423     return value.definition != null && value.isConstant();
424   }
425 
numberOfUsesWithConstraint()426   public int numberOfUsesWithConstraint() {
427     int count = 0;
428     for (LiveIntervalsUse use : getUses()) {
429       if (use.hasConstraint()) {
430         count++;
431       }
432     }
433     return count;
434   }
435 
436   @Override
toString()437   public String toString() {
438     StringBuilder builder = new StringBuilder();
439     builder.append("(cons ");
440     // Use the field here to avoid toString to have side effects.
441     builder.append(numberOfConsecutiveRegisters);
442     builder.append("): ");
443     for (LiveRange range : getRanges()) {
444       builder.append(range);
445       builder.append(" ");
446     }
447     builder.append("\n");
448     return builder.toString();
449   }
450 
toAscciArtString()451   public String toAscciArtString() {
452     StringBuilder builder = new StringBuilder();
453     int current = 0;
454     for (LiveRange range : getRanges()) {
455       if (range.isInfinite()) {
456         builder.append("--- infinite ---...");
457         break;
458       }
459       for (; current < range.start; current++) {
460         builder.append(" ");
461       }
462       for (; current < range.end; current++) {
463         builder.append("-");
464       }
465     }
466     return builder.toString();
467   }
468 
print(CfgPrinter printer, int number, int parentNumber)469   public void print(CfgPrinter printer, int number, int parentNumber) {
470     printer.append(number * 10000 + register) // range number
471         .sp().append("object") // range type
472         .sp().append(parentNumber * 10000 + getSplitParent().getRegister()) // split parent
473         .sp().append(-1); // hint
474     for (LiveRange range : getRanges()) {
475       printer.sp().append(range.toString());
476     }
477     for (LiveIntervalsUse use : getUses()) {
478       printer.sp().append(use.getPosition()).sp().append("M");
479     }
480     printer.append(" \"\"").ln();
481     int delta = 0;
482     for (LiveIntervals splitChild : splitChildren) {
483       delta += 10000;
484       splitChild.print(printer, number + delta, number);
485     }
486   }
487 }
488