/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.compilers.opt.regalloc;
import java.util.Iterator;
import java.util.SortedSet;
import org.jikesrvm.VM;
import org.jikesrvm.compilers.opt.ir.BasicBlock;
import org.jikesrvm.compilers.opt.ir.Instruction;
import org.jikesrvm.compilers.opt.ir.Register;
/**
* Implements a live interval with holes; i.e. an ordered set of basic live
* intervals. There is exactly one instance of this class for each
* Register.
* <p>
* The order that this set imposes is inconsistent with equals.
* <p>
* This class is designed for use by a single thread.
*/
class CompoundInterval extends IncreasingStartIntervalSet {
/** Support for Set serialization */
static final long serialVersionUID = 7423553044815762071L;
/**
* Is this compound interval fully contained in infrequent code?
*/
private boolean _infrequent = true;
final void setFrequent() {
_infrequent = false;
}
final boolean isInfrequent() {
return _infrequent;
}
/**
* The register this compound interval represents or {@code null}
* if this interval is not associated with a register (i.e. if it
* represents a spill location).
*/
private final Register reg;
/**
* A spill location assigned for this interval.
*/
private SpillLocationInterval spillInterval;
SpillLocationInterval getSpillInterval() {
return spillInterval;
}
/**
* @return the register this interval represents
*/
Register getRegister() {
return reg;
}
/**
* Creates a new compound interval of a single Basic interval.
*
* @param dfnBegin interval's begin
* @param dfnEnd interval's end
* @param register the register for the compound interval
*/
CompoundInterval(int dfnBegin, int dfnEnd, Register register) {
BasicInterval newInterval = new MappedBasicInterval(dfnBegin, dfnEnd, this);
add(newInterval);
reg = register;
}
/**
* Creates a new compound interval of a single Basic interval.
*
* @param i interval providing start and end for the new interval
* @param register the register for the compound interval
*/
CompoundInterval(BasicInterval i, Register register) {
BasicInterval newInterval = new MappedBasicInterval(i.getBegin(), i.getEnd(), this);
add(newInterval);
reg = register;
}
/**
* Dangerous constructor: use with care.
* <p>
* Creates a compound interval with a register but doesn't actually
* add any intervals to the compound interval.
*
* @param r a register
*/
protected CompoundInterval(Register r) {
reg = r;
}
/**
* Copies the ranges from this interval into a new interval associated
* with a register.
*
* @param r the register for the new interval
* @return the new interval
*/
CompoundInterval copy(Register r) {
CompoundInterval result = new CompoundInterval(r);
for (Iterator<BasicInterval> i = iterator(); i.hasNext();) {
BasicInterval b = i.next();
result.add(b);
}
return result;
}
/**
* Copies the basic intervals up to and including stop into a new interval.
* The new interval is associated with the given register.
*
* @param r the register for the new interval
* @param stop the interval to stop at
* @return the new interval
*/
CompoundInterval copy(Register r, BasicInterval stop) {
CompoundInterval result = new CompoundInterval(r);
for (Iterator<BasicInterval> i = iterator(); i.hasNext();) {
BasicInterval b = i.next();
result.add(b);
if (b.sameRange(stop)) return result;
}
return result;
}
/**
* Add a new live range to this compound interval.
* @param regAllocState depth-first numbers for for instructions
* @param live the new live range
* @param bb the basic block for live
*
* @return the new basic interval if one was created; null otherwise
*/
BasicInterval addRange(RegisterAllocatorState regAllocState, LiveIntervalElement live, BasicBlock bb) {
if (shouldConcatenate(regAllocState, live, bb)) {
// concatenate with the last basic interval
BasicInterval last = last();
last.setEnd(regAllocState.getDfnEnd(live, bb));
return null;
} else {
// create a new basic interval and append it to the list.
BasicInterval newInterval = new MappedBasicInterval(regAllocState.getDfnBegin(live, bb), regAllocState.getDfnEnd(live, bb), this);
add(newInterval);
return newInterval;
}
}
/**
* Should we simply merge the live interval <code>live</code> into a
* previous BasicInterval?
* @param regAllocState depth-first numbers for for instructions
* @param live the live interval being queried
* @param bb the basic block in which live resides.
*
* @return {@code true} if the interval should be concatenated, {@code false}
* if it should'nt
*/
private boolean shouldConcatenate(RegisterAllocatorState regAllocState, LiveIntervalElement live, BasicBlock bb) {
BasicInterval last = last();
// Make sure the new live range starts after the last basic interval
if (VM.VerifyAssertions) {
VM._assert(last.getEnd() <= regAllocState.getDfnBegin(live, bb));
}
int dfnBegin = regAllocState.getDfnBegin(live, bb);
if (live.getBegin() != null) {
if (live.getBegin() == bb.firstRealInstruction()) {
// live starts the basic block. Now make sure it is contiguous
// with the last interval.
return last.getEnd() + 1 >= dfnBegin;
} else {
// live does not start the basic block. Merge with last iff
// last and live share an instruction. This happens when a
// register is def'ed and use'd in the same instruction.
return last.getEnd() == dfnBegin;
}
} else {
// live.getBegin == null.
// Merge if it is contiguous with the last interval.
int dBegin = regAllocState.getDFN(bb.firstInstruction());
return last.getEnd() + 1 >= dBegin;
}
}
/**
* Assign this compound interval to a free spill location.
*
* @param spillManager governing spill location manager
* @param regAllocState current state of the register allocator
*/
void spill(SpillLocationManager spillManager, RegisterAllocatorState regAllocState) {
spillInterval = spillManager.findOrCreateSpillLocation(this);
regAllocState.setSpill(reg, spillInterval.getOffset());
regAllocState.clearOneToOne(reg);
if (LinearScan.VERBOSE_DEBUG) {
System.out.println("Assigned " + reg + " to location " + spillInterval.getOffset());
}
}
boolean isSpilled(RegisterAllocatorState regAllocState) {
return (regAllocState.getSpill(getRegister()) != 0);
}
/**
* Assign this compound interval to a physical register.
*
* @param r the register to assign to
*/
void assign(Register r) {
getRegister().allocateToRegister(r);
}
/**
* @param regAllocState current state of the register allocator
* @return {@code true} if this interval has been assigned to
* a physical register
*/
boolean isAssigned(RegisterAllocatorState regAllocState) {
return (regAllocState.getMapping(getRegister()) != null);
}
/**
* @param regAllocState current state of the register allocator
* @return the physical register this interval is assigned to, {@code null}
* if none assigned
*/
Register getAssignment(RegisterAllocatorState regAllocState) {
return regAllocState.getMapping(getRegister());
}
/**
* Merges this interval with another, non-intersecting interval.
* <p>
* Precondition: BasicInterval stop is an interval in i. This version
* will only merge basic intervals up to and including stop into this.
*
* @param i a non-intersecting interval for merging
* @param stop a interval to stop at
*/
void addNonIntersectingInterval(CompoundInterval i, BasicInterval stop) {
SortedSet<BasicInterval> headSet = i.headSetInclusive(stop);
addAll(headSet);
}
/**
* Computes the headSet() [from java.util.SortedSet] but includes all
* elements less than upperBound <em>inclusive</em>.
*
* @param upperBound the interval acting as upper bound
* @return the head set
* @see SortedSet#headSet(Object)
*/
SortedSet<BasicInterval> headSetInclusive(BasicInterval upperBound) {
BasicInterval newUpperBound = new BasicInterval(upperBound.getBegin() + 1, upperBound.getEnd());
return headSet(newUpperBound);
}
/**
* Computes the headSet() [from java.util.SortedSet] but includes all
* elements less than upperBound <em>inclusive</em>.
*
* @param upperBound the instruction number acting as upper bound
* @return the head set
* @see SortedSet#headSet(Object)
*/
SortedSet<BasicInterval> headSetInclusive(int upperBound) {
BasicInterval newUpperBound = new BasicInterval(upperBound + 1, upperBound + 1);
return headSet(newUpperBound);
}
/**
* Computes the tailSet() [from java.util.SortedSet] but includes all
* elements greater than lowerBound <em>inclusive</em>.
*
* @param lowerBound the instruction number acting as lower bound
* @return the tail set
* @see SortedSet#tailSet(Object)
*/
SortedSet<BasicInterval> tailSetInclusive(int lowerBound) {
BasicInterval newLowerBound = new BasicInterval(lowerBound - 1, lowerBound - 1);
return tailSet(newLowerBound);
}
/**
* Removes some basic intervals from this compound interval, and returns
* the intervals actually removed.
* <p>
* PRECONDITION: All basic intervals in the other interval that have the
* same begin as an interval in this compound interval must have the same
* end. For example, for a compound interval {@code [(1,2)(2,3)]},
* the other interval would be allowed to contain {@code (1,2)} and/or
* {@code (2,2)} but not {@code (1,3)}.
* <p>
* A violation of the precondition that would have an effect will trigger
* an assertion failure in assertion-enabled builds.
*
* @param other interval to check for intervals that we want to remove
* from this
* @return the basic intervals that were removed
*/
CompoundInterval removeIntervalsAndCache(CompoundInterval other) {
CompoundInterval result = new CompoundInterval(other.getRegister());
Iterator<BasicInterval> myIterator = iterator();
Iterator<BasicInterval> otherIterator = other.iterator();
BasicInterval current = myIterator.hasNext() ? myIterator.next() : null;
BasicInterval otherCurrent = otherIterator.hasNext() ? otherIterator.next() : null;
while (otherCurrent != null && current != null) {
if (current.startsBefore(otherCurrent)) {
current = myIterator.hasNext() ? myIterator.next() : null;
} else if (otherCurrent.startsBefore(current)) {
otherCurrent = otherIterator.hasNext() ? otherIterator.next() : null;
} else {
if (VM.VerifyAssertions) VM._assert(current.sameRange(otherCurrent));
otherCurrent = otherIterator.hasNext() ? otherIterator.next() : null;
BasicInterval next = myIterator.hasNext() ? myIterator.next() : null;
// add the interval to the cache
result.add(current);
current = next;
}
}
removeAll(result);
return result;
}
/**
* @return the lowest DFN in this compound interval at this time
*/
int getLowerBound() {
BasicInterval b = first();
return b.getBegin();
}
/**
* @return the highest DFN in this compound interval at this time
*/
int getUpperBound() {
BasicInterval b = last();
return b.getEnd();
}
boolean intersects(CompoundInterval i) {
if (isEmpty()) return false;
if (i.isEmpty()) return false;
// Walk over the basic intervals of this interval and i.
// Restrict the walking to intervals that might intersect.
int lower = Math.max(getLowerBound(), i.getLowerBound());
int upper = Math.min(getUpperBound(), i.getUpperBound());
// we may have to move one interval lower on each side.
BasicInterval b = getBasicInterval(lower);
int myLower = (b == null) ? lower : b.getBegin();
if (myLower > upper) return false;
b = i.getBasicInterval(lower);
int otherLower = (b == null) ? lower : b.getBegin();
if (otherLower > upper) return false;
SortedSet<BasicInterval> myTailSet = tailSetInclusive(myLower);
SortedSet<BasicInterval> otherTailSet = i.tailSetInclusive(otherLower);
Iterator<BasicInterval> myIterator = myTailSet.iterator();
Iterator<BasicInterval> otherIterator = otherTailSet.iterator();
BasicInterval current = myIterator.hasNext() ? myIterator.next() : null;
BasicInterval currentI = otherIterator.hasNext() ? otherIterator.next() : null;
while (current != null && currentI != null) {
if (current.getBegin() > upper) break;
if (currentI.getBegin() > upper) break;
if (current.intersects(currentI)) return true;
if (current.startsBefore(currentI)) {
current = myIterator.hasNext() ? myIterator.next() : null;
} else {
currentI = otherIterator.hasNext() ? otherIterator.next() : null;
}
}
return false;
}
/**
* @param dfnNumbers depth-first numbers for for instructions
* @param s The instruction in question
* @return the first basic interval that contains a given
* instruction, {@code null} if there is no such interval
*/
BasicInterval getBasicInterval(RegisterAllocatorState dfnNumbers, Instruction s) {
return getBasicInterval(dfnNumbers.getDFN(s));
}
/**
* @param n The DFN of the instruction in question
* @return the first basic interval that contains a given
* instruction, {@code null} if there is no such interval
*/
BasicInterval getBasicInterval(int n) {
SortedSet<BasicInterval> headSet = headSetInclusive(n);
if (!headSet.isEmpty()) {
BasicInterval last = headSet.last();
return last.contains(n) ? last : null;
} else {
return null;
}
}
@Override
public String toString() {
StringBuilder str = new StringBuilder("[");
str.append(getRegister());
str.append("]:");
for (Iterator<BasicInterval> i = iterator(); i.hasNext();) {
BasicInterval b = i.next();
str.append(b);
}
return str.toString();
}
}