/* * 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 static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.jikesrvm.compilers.opt.ir.Operators.*; import org.jikesrvm.compilers.opt.ir.BasicBlock; import org.jikesrvm.compilers.opt.ir.ControlFlowGraph; import org.jikesrvm.compilers.opt.ir.Empty; import org.jikesrvm.compilers.opt.ir.Instruction; import org.jikesrvm.compilers.opt.ir.Register; import org.jikesrvm.junit.runners.RequiresOptCompiler; import org.jikesrvm.junit.runners.VMRequirements; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; @RunWith(VMRequirements.class) @Category({RequiresOptCompiler.class}) public class CompoundIntervalTest { private static final int DEFAULT_BEGIN = 1; private static final int DEFAULT_END = 2; private static final int SPILL_INTERVAL_OFFSET = -23; private static final int SPILL_INTERVAL_SIZE = 17; @Test public void compoundIntervalConstructorDoesNotCheckForNull() { createCompoundIntervalWithoutRegister(); } @Test public void compoundIntervalsAreInfrequentByDefault() { CompoundInterval ci = createCompoundIntervalWithoutRegister(); assertThat(ci.isInfrequent(), is(true)); } @Test public void compoundIntervalsCanBeMadeFrequent() { CompoundInterval ci = createCompoundIntervalWithoutRegister(); ci.setFrequent(); assertThat(ci.isInfrequent(), is(false)); } @Test public void compoundIntervalsDoNotHaveASpillIntervalByDefault() { CompoundInterval ci = createCompoundIntervalWithoutRegister(); assertNull(ci.getSpillInterval()); } @Test public void spillingOfCompoundIntervalSetsTheSpillInterval() { Register r = new Register(0); CompoundInterval ci = new CompoundInterval(DEFAULT_BEGIN, DEFAULT_END, r); RegisterAllocatorState regAllocState = new RegisterAllocatorState(1); assertThat(ci.isSpilled(regAllocState), is(false)); ci.spill(new MockSpillLocationManager(), regAllocState); assertThat(ci.getSpillInterval().getOffset(), is(SPILL_INTERVAL_OFFSET)); assertThat(ci.getSpillInterval().getSize(),is(SPILL_INTERVAL_SIZE)); assertThat(ci.isSpilled(regAllocState), is(true)); } @Test public void assignCausesAssignmentOfTheIntervalsRegisterToTheGivenRegister() { Register r = new Register(0); CompoundInterval ci = new CompoundInterval(DEFAULT_BEGIN, DEFAULT_END, r); Register s = new Register(1); RegisterAllocatorState regAllocState = new RegisterAllocatorState(0); assertThat(ci.isAssigned(regAllocState), is(false)); assertNull(ci.getAssignment(regAllocState)); ci.assign(s); assertThat(s.mapsToRegister, is(r)); assertThat(!s.isSpilled() && s.isTouched() && s.isAllocated(), is(true)); assertThat(r.mapsToRegister, is(s)); assertThat(!r.isSpilled() && r.isTouched() && r.isAllocated(), is(true)); assertThat(ci.isAssigned(regAllocState), is(true)); assertThat(ci.getAssignment(regAllocState), is(s)); } @Test public void getBasicIntervalReturnsNullIfNoMatchingIntervalIsFound() { CompoundInterval ci = createCompoundIntervalWithoutRegister(); assertNull(ci.getBasicInterval(10)); } @Test public void getBasicIntervalReturnsAnIntervalIfOneMatches() { CompoundInterval ci = createCompoundIntervalWithoutRegister(); BasicInterval bi = ci.getBasicInterval(DEFAULT_END); assertThat(bi.getBegin(), is(DEFAULT_BEGIN)); assertThat(bi.getEnd(), is(DEFAULT_END)); } @Test public void getBasicIntervalReturnsIntervalWithGreatestStartIfMultipleMatch() { CompoundInterval ci = new CompoundInterval(DEFAULT_BEGIN, DEFAULT_END, null); ci.add(new BasicInterval(DEFAULT_BEGIN, DEFAULT_END + 1)); BasicInterval bi = ci.getBasicInterval(DEFAULT_END); assertThat(bi.getBegin(), is(DEFAULT_BEGIN)); assertThat(bi.getEnd(), is(DEFAULT_END + 1)); } @Test public void getBasicIntervalRegAllocStateReturnsNullIfNoMatchingIntervalIsFound() { CompoundInterval ci = createCompoundIntervalWithoutRegister(); RegisterAllocatorState regAllocState = new RegisterAllocatorState(1); regAllocState.initializeDepthFirstNumbering(20); Instruction writeFloor = Empty.create(WRITE_FLOOR); regAllocState.setDFN(writeFloor, DEFAULT_BEGIN); ci.getBasicInterval(regAllocState, writeFloor); assertNull(ci.getBasicInterval(10)); } @Test public void getBasicIntervalRegAllocStateReturnsAnIntervalIfOneMatches() { CompoundInterval ci = createCompoundIntervalWithoutRegister(); RegisterAllocatorState regAllocState = new RegisterAllocatorState(1); regAllocState.initializeDepthFirstNumbering(20); Instruction writeFloor = Empty.create(WRITE_FLOOR); regAllocState.setDFN(writeFloor, DEFAULT_END); BasicInterval bi = ci.getBasicInterval(regAllocState, writeFloor); assertThat(bi.getBegin(), is(DEFAULT_BEGIN)); assertThat(bi.getEnd(), is(DEFAULT_END)); } @Test public void getBasicIntervalRegAllocStateReturnsIntervalWithGreatestStartIfMultipleMatch() { CompoundInterval ci = new CompoundInterval(DEFAULT_BEGIN, DEFAULT_END, null); ci.add(new BasicInterval(DEFAULT_BEGIN, DEFAULT_END + 1)); RegisterAllocatorState regAllocState = new RegisterAllocatorState(1); regAllocState.initializeDepthFirstNumbering(20); Instruction writeFloor = Empty.create(WRITE_FLOOR); regAllocState.setDFN(writeFloor, DEFAULT_END); BasicInterval bi = ci.getBasicInterval(regAllocState, writeFloor); assertThat(bi.getBegin(), is(DEFAULT_BEGIN)); assertThat(bi.getEnd(), is(DEFAULT_END + 1)); } @Test public void addNonIntersectingSetAddsAllIntervalsInTheOtherCompoundInterval() { CompoundInterval ci = createCompoundIntervalWithoutRegister(); CompoundInterval toMerge = new CompoundInterval(DEFAULT_END + 1, DEFAULT_END + 2, null); BasicInterval otherInterval = new BasicInterval(DEFAULT_END + 1, DEFAULT_END + 3); toMerge.add(otherInterval); BasicInterval otherInterval2 = new BasicInterval(DEFAULT_END + 2, DEFAULT_END + 4); toMerge.add(otherInterval2); BasicInterval stop = new BasicInterval(DEFAULT_END + 5, DEFAULT_END + 6); ci.addNonIntersectingInterval(toMerge, stop); assertThat(ci.size(), is(4)); assertThat(ci.getLowerBound(), is(DEFAULT_BEGIN)); assertThat(ci.getUpperBound(), is(DEFAULT_END + 4)); } @Test public void addNonIntersectingSetStopsAtTheStopInterval() { CompoundInterval ci = createCompoundIntervalWithoutRegister(); CompoundInterval toMerge = new CompoundInterval(DEFAULT_END + 1, DEFAULT_END + 2, null); BasicInterval otherInterval = new BasicInterval(DEFAULT_END + 1, DEFAULT_END + 3); toMerge.add(otherInterval); BasicInterval otherInterval2 = new BasicInterval(DEFAULT_END + 2, DEFAULT_END + 4); toMerge.add(otherInterval2); BasicInterval stop = new BasicInterval(DEFAULT_END + 1, DEFAULT_END + 3); ci.addNonIntersectingInterval(toMerge, stop); assertThat(ci.size(), is(3)); assertThat(ci.getLowerBound(), is(DEFAULT_BEGIN)); assertThat(ci.getUpperBound(), is(DEFAULT_END + 3)); } @Test public void addNonIntersectingStopIntervalNeedNotBeInIntervalThatsMerged() { CompoundInterval ci = createCompoundIntervalWithoutRegister(); CompoundInterval toMerge = new CompoundInterval(DEFAULT_END + 1, DEFAULT_END + 2, null); BasicInterval otherInterval = new BasicInterval(DEFAULT_END + 1, DEFAULT_END + 3); toMerge.add(otherInterval); BasicInterval otherInterval2 = new BasicInterval(DEFAULT_END + 2, DEFAULT_END + 4); toMerge.add(otherInterval2); BasicInterval stop = new BasicInterval(DEFAULT_END + 1, DEFAULT_END + 4); ci.addNonIntersectingInterval(toMerge, stop); assertThat(ci.size(), is(3)); assertThat(ci.getLowerBound(), is(DEFAULT_BEGIN)); assertThat(ci.getUpperBound(), is(DEFAULT_END + 3)); } @Test public void copyCopiesAllIntervalsToNewCompoundIntervalWithRightRegister() { BasicInterval bi = new BasicInterval(DEFAULT_BEGIN, DEFAULT_END); CompoundInterval toCopy = new CompoundInterval(bi, null); BasicInterval bi2 = new BasicInterval(DEFAULT_END, DEFAULT_END + 1); BasicInterval bi3 = new BasicInterval(DEFAULT_END + 1, DEFAULT_END + 2); toCopy.add(bi2); toCopy.add(bi3); Register r = new Register(2); CompoundInterval copy = toCopy.copy(r); assertThat(copy.getRegister(), is(r)); assertThat(copy.contains(bi), is(true)); assertThat(copy.contains(bi2), is(true)); assertThat(copy.contains(bi3), is(true)); assertThat(copy.size(), is(3)); } @Test public void copyStopsAtTheRightPointWhenRequired() { BasicInterval bi = new BasicInterval(DEFAULT_BEGIN, DEFAULT_END); CompoundInterval toCopy = new CompoundInterval(bi, null); BasicInterval bi2 = new BasicInterval(DEFAULT_END, DEFAULT_END + 1); BasicInterval bi3 = new BasicInterval(DEFAULT_END + 1, DEFAULT_END + 2); toCopy.add(bi2); toCopy.add(bi3); Register r = new Register(2); CompoundInterval copy = toCopy.copy(r, bi2); assertThat(copy.getRegister(), is(r)); assertThat(copy.contains(bi), is(true)); assertThat(copy.contains(bi2), is(true)); assertThat(copy.contains(bi3), is(false)); assertThat(copy.size(), is(2)); } @Test public void copyMethodsReallyReturnCopies() { BasicInterval bi = new BasicInterval(DEFAULT_BEGIN, DEFAULT_END); CompoundInterval toCopy = new CompoundInterval(bi, null); BasicInterval bi2 = new BasicInterval(DEFAULT_END, DEFAULT_END + 1); toCopy.add(bi2); CompoundInterval aCopy = toCopy.copy(new Register(1)); assertThat(aCopy, not(sameInstance(toCopy))); CompoundInterval anotherCopy = toCopy.copy(new Register(3), bi); assertThat(anotherCopy, not(sameInstance(toCopy))); } @Test public void addRangeChangesEndOfLastIntervalWhenRangesDirectlyFollowEachOther_NoDefUse() { Register reg = new Register(3); CompoundInterval ci = new CompoundInterval(DEFAULT_BEGIN, DEFAULT_END, reg); assertThat(ci.last().getEnd(), is(DEFAULT_END)); RegisterAllocatorState regAllocState = new RegisterAllocatorState(1); LiveIntervalElement live = new LiveIntervalElement(reg, null, null); ControlFlowGraph emptyCfg = new ControlFlowGraph(0); BasicBlock bb = new BasicBlock(1, null, emptyCfg); regAllocState.initializeDepthFirstNumbering(10); regAllocState.setDFN(bb.firstInstruction(), DEFAULT_END); regAllocState.setDFN(bb.lastInstruction(), DEFAULT_END + 1); BasicInterval bi = ci.addRange(regAllocState, live, bb); assertNull(bi); assertThat(ci.last().getEnd(), is(DEFAULT_END + 1)); } @Test public void addRangeChangesEndOfLastIntervalWhenRangesDirectlyFollowEachOther_DefUse() { Register reg = new Register(3); CompoundInterval ci = new CompoundInterval(DEFAULT_BEGIN, DEFAULT_END, reg); assertThat(ci.last().getEnd(), is(DEFAULT_END)); RegisterAllocatorState regAllocState = new RegisterAllocatorState(1); Instruction def = Empty.create(WRITE_FLOOR); Instruction lastUse = Empty.create(WRITE_FLOOR); LiveIntervalElement live = new LiveIntervalElement(reg, def, lastUse); ControlFlowGraph emptyCfg = new ControlFlowGraph(0); BasicBlock bb = new BasicBlock(1, null, emptyCfg); bb.appendInstruction(def); bb.appendInstruction(lastUse); regAllocState.initializeDepthFirstNumbering(10); regAllocState.setDFN(def, DEFAULT_END); regAllocState.setDFN(lastUse, DEFAULT_END + 1); BasicInterval bi = ci.addRange(regAllocState, live, bb); assertNull(bi); assertThat(ci.last().getEnd(), is(DEFAULT_END + 1)); } @Test public void addRangeChangesEndOfLastIntervalWhenRangesDirectlyFollowEachOther_DefUseSameInstruction() { Register reg = new Register(3); CompoundInterval ci = new CompoundInterval(DEFAULT_BEGIN, DEFAULT_END, reg); assertThat(ci.last().getEnd(), is(DEFAULT_END)); RegisterAllocatorState regAllocState = new RegisterAllocatorState(1); Instruction empty = Empty.create(WRITE_FLOOR); Instruction defUse = Empty.create(WRITE_FLOOR); Instruction lastUse = Empty.create(WRITE_FLOOR); LiveIntervalElement live = new LiveIntervalElement(reg, defUse, lastUse); ControlFlowGraph emptyCfg = new ControlFlowGraph(0); BasicBlock bb = new BasicBlock(1, null, emptyCfg); bb.appendInstruction(empty); bb.appendInstruction(defUse); bb.appendInstruction(lastUse); regAllocState.initializeDepthFirstNumbering(10); regAllocState.setDFN(empty, DEFAULT_BEGIN); regAllocState.setDFN(defUse, DEFAULT_END); regAllocState.setDFN(lastUse, DEFAULT_END + 1); BasicInterval bi = ci.addRange(regAllocState, live, bb); assertNull(bi); assertThat(ci.last().getEnd(), is(DEFAULT_END + 1)); } @Test public void addRangeCreatesNewIntervalWhenThereIsAGapBetweenRanges() { Register reg = new Register(3); CompoundInterval ci = new CompoundInterval(DEFAULT_BEGIN, DEFAULT_END, reg); assertThat(ci.last().getEnd(), is(DEFAULT_END)); RegisterAllocatorState regAllocState = new RegisterAllocatorState(1); LiveIntervalElement live = new LiveIntervalElement(reg, null, null); ControlFlowGraph emptyCfg = new ControlFlowGraph(0); BasicBlock bb = new BasicBlock(1, null, emptyCfg); regAllocState.initializeDepthFirstNumbering(10); regAllocState.setDFN(bb.firstInstruction(), DEFAULT_END + 2); regAllocState.setDFN(bb.lastInstruction(), DEFAULT_END + 3); BasicInterval bi = ci.addRange(regAllocState, live, bb); assertThat(ci.last(), sameInstance(bi)); } @Test public void headSetInclusiveIncludesBorderInterval() { CompoundInterval ci = createCompoundIntervalWithoutRegister(); ci.add(new MappedBasicInterval(DEFAULT_END + 1, DEFAULT_END + 2, null)); ci.add(new MappedBasicInterval(DEFAULT_END + 2, DEFAULT_END + 3, null)); BasicInterval upperBound = new BasicInterval(DEFAULT_END + 2, DEFAULT_END + 3); assertThat(ci.headSetInclusive(upperBound).contains(upperBound), is(true)); } @Test public void headSetInclusiveIncludesIntervalWithUpperBound() { CompoundInterval ci = createCompoundIntervalWithoutRegister(); ci.add(new MappedBasicInterval(DEFAULT_END + 1, DEFAULT_END + 2, null)); ci.add(new MappedBasicInterval(DEFAULT_END + 2, DEFAULT_END + 3, null)); BasicInterval upperBound = new BasicInterval(DEFAULT_END + 2, DEFAULT_END + 3); assertThat(ci.headSetInclusive(DEFAULT_END + 3).contains(upperBound), is(true)); } @Test public void tailSetInclusiveIncludesIntervalWithLowerBound() { CompoundInterval ci = createCompoundIntervalWithoutRegister(); ci.add(new MappedBasicInterval(DEFAULT_END + 1, DEFAULT_END + 2, null)); ci.add(new MappedBasicInterval(DEFAULT_END + 2, DEFAULT_END + 3, null)); BasicInterval lowerBound = new BasicInterval(DEFAULT_BEGIN, DEFAULT_END); assertThat(ci.tailSetInclusive(DEFAULT_BEGIN).contains(lowerBound), is(true)); } @Test public void removeIntervalsAndCacheDoesNothingWhenOtherIntervalsIsEmpty() { CompoundInterval ci = createCompoundIntervalWithoutRegister(); ci.add(new MappedBasicInterval(DEFAULT_END + 1, DEFAULT_END + 2, null)); ci.add(new MappedBasicInterval(DEFAULT_END + 2, DEFAULT_END + 3, null)); CompoundInterval cached = ci.removeIntervalsAndCache(new CompoundInterval(null)); assertThat(ci.size(), is(3)); assertThat(cached.isEmpty(), is(true)); } @Test public void removeIntervalsAndCacheDoesNothingWhenIntervalEndsAfterThisOne() { CompoundInterval ci = createCompoundIntervalWithoutRegister(); ci.add(new MappedBasicInterval(DEFAULT_END + 1, DEFAULT_END + 2, null)); ci.add(new MappedBasicInterval(DEFAULT_END + 2, DEFAULT_END + 3, null)); CompoundInterval cached = ci.removeIntervalsAndCache(new CompoundInterval(DEFAULT_END + 3, DEFAULT_END + 6, null)); assertThat(ci.size(), is(3)); assertThat(cached.isEmpty(), is(true)); } @Test public void removeIntervalsAndCacheWorksWhenOtherIntervalIsASubset() { CompoundInterval ci = createCompoundIntervalWithoutRegister(); ci.add(new MappedBasicInterval(DEFAULT_END + 1, DEFAULT_END + 2, null)); ci.add(new MappedBasicInterval(DEFAULT_END + 2, DEFAULT_END + 3, null)); ci.add(new MappedBasicInterval(DEFAULT_END + 1, DEFAULT_END + 4, null)); ci.add(new MappedBasicInterval(DEFAULT_BEGIN, DEFAULT_END + 1, null)); CompoundInterval toRemove = new CompoundInterval(DEFAULT_END + 1, DEFAULT_END + 2, null); toRemove.add(new MappedBasicInterval(DEFAULT_BEGIN, DEFAULT_END, null)); CompoundInterval cached = ci.removeIntervalsAndCache(toRemove); assertThat(cached.size(), is(2)); assertThat(cached.contains(new MappedBasicInterval(DEFAULT_BEGIN, DEFAULT_END, null)), is(true)); assertThat(cached.contains(new MappedBasicInterval(DEFAULT_END + 1, DEFAULT_END + 2, null)), is(true)); assertThat(ci.size(), is(3)); assertThat(ci.contains(new MappedBasicInterval(DEFAULT_BEGIN, DEFAULT_END + 1, null)), is(true)); assertThat(ci.contains(new MappedBasicInterval(DEFAULT_END + 1, DEFAULT_END + 4, null)), is(true)); assertThat(ci.contains(new MappedBasicInterval(DEFAULT_END + 2, DEFAULT_END + 3, null)), is(true)); } @Test public void emptyCompoundIntervalsDoNotIntersectWithAnything() { CompoundInterval empty = new CompoundInterval(null); assertThat(empty.intersects(new CompoundInterval(null)), is(false)); } @Test public void nonEmptyCompoundIntervalsIntersectWithThemselves() { CompoundInterval ci = createCompoundIntervalWithoutRegister(); assertThat(ci.intersects(ci), is(true)); } @Test public void registersDoNotMatterForIntersection() { CompoundInterval ci = new CompoundInterval(DEFAULT_BEGIN, DEFAULT_END, new Register(-1)); CompoundInterval otherCi = new CompoundInterval(DEFAULT_BEGIN, DEFAULT_END, new Register(-2)); assertThat(ci.intersects(otherCi), is(true)); } @Test public void compoundIntervalsDoNotIntersectWithEmptyIntervals() { CompoundInterval ci = createCompoundIntervalWithoutRegister(); assertThat(ci.intersects(new CompoundInterval(null)), is(false)); } @Test public void intervalsDoNotIntersectWhenFirstIntervalIsLowerThanTheOther() { CompoundInterval ci = createCompoundIntervalWithoutRegister(); CompoundInterval other = new CompoundInterval(DEFAULT_END + 4, DEFAULT_END + 6, null); assertThat(ci.intersects(other), is(false)); } @Test public void intervalsDoNotIntersectWhenFirstIntervalIsHigherThanTheOther() { CompoundInterval ci = new CompoundInterval(DEFAULT_END + 4, DEFAULT_END + 6, null); CompoundInterval other = createCompoundIntervalWithoutRegister(); assertThat(ci.intersects(other), is(false)); } @Test public void intervalsDoNotIntersectWhenContainedIntervalsDoNotIntersect() { CompoundInterval ci = new CompoundInterval(DEFAULT_BEGIN, DEFAULT_END, null); ci.add(new MappedBasicInterval(DEFAULT_END + 2, DEFAULT_END + 3, null)); ci.add(new MappedBasicInterval(DEFAULT_END + 5, DEFAULT_END + 7, null)); CompoundInterval other = new CompoundInterval(DEFAULT_END, DEFAULT_END + 1, null); other.add(new MappedBasicInterval(DEFAULT_END + 1, DEFAULT_END + 2, null)); other.add(new MappedBasicInterval(DEFAULT_END + 3, DEFAULT_END + 4, null)); assertThat(ci.intersects(other), is(false)); } @Test public void intervalsDoNotIntersectWhenContainedIntervalsDoNotIntersectOtherWayRound() { CompoundInterval ci = new CompoundInterval(DEFAULT_END, DEFAULT_END + 1, null); ci.add(new MappedBasicInterval(DEFAULT_END + 1, DEFAULT_END + 2, null)); ci.add(new MappedBasicInterval(DEFAULT_END + 3, DEFAULT_END + 4, null)); CompoundInterval other = new CompoundInterval(1, DEFAULT_END, null); other.add(new MappedBasicInterval(DEFAULT_END + 2, DEFAULT_END + 3, null)); other.add(new MappedBasicInterval(DEFAULT_END + 5, DEFAULT_END + 7, null)); assertThat(ci.intersects(other), is(false)); } private CompoundInterval createCompoundIntervalWithoutRegister() { return new CompoundInterval(DEFAULT_BEGIN, DEFAULT_END, null); } private static class MockSpillLocationManager extends SpillLocationManager { MockSpillLocationManager() { super(null); } @Override SpillLocationInterval findOrCreateSpillLocation(CompoundInterval ci) { return new SpillLocationInterval(SPILL_INTERVAL_OFFSET, SPILL_INTERVAL_SIZE, 0); } } }