/* * 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.is; import static org.jikesrvm.compilers.opt.ir.Operators.FENCE; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import org.jikesrvm.classloader.NormalMethod; import org.jikesrvm.compilers.opt.OptOptions; import org.jikesrvm.compilers.opt.OptimizingCompilerException; import org.jikesrvm.compilers.opt.ir.Empty; import org.jikesrvm.compilers.opt.ir.IR; import org.jikesrvm.compilers.opt.ir.Instruction; import org.jikesrvm.compilers.opt.ir.MIRInfo; import org.jikesrvm.compilers.opt.ir.Register; import org.jikesrvm.junit.runners.RequiresBuiltJikesRVM; import org.jikesrvm.junit.runners.RequiresOptCompiler; import org.jikesrvm.junit.runners.VMRequirements; import org.jikesrvm.tests.util.MethodsForTests; import org.jikesrvm.tests.util.TestingTools; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; @RunWith(VMRequirements.class) @Category({RequiresBuiltJikesRVM.class, RequiresOptCompiler.class}) public class ActiveSetTest { //TODO tests for the most important methods are still missing: // the findAvailableRegister(*) methods and the allocate method currently // do not have any useful tests. private static final int SPILL_LOCATION = -23; private static final int UNUSED_REG_NUMBER = -2; private static final int FIRST_REG_NUMBER = 100; private static final int SECOND_REG_NUMBER = 101; private static final int MAXIMUM_REGISTER_NUMBER = 102; private SpillLocationManager spillLocations; private ActiveSet createActiveSet() throws Exception { return createActiveSetFromRegisterAllocatorState(null); } private ActiveSet createActiveSetFromRegisterAllocatorState( RegisterAllocatorState state) throws Exception { NormalMethod nm = TestingTools.getNormalMethod(MethodsForTests.class, "emptyStaticMethodWithoutAnnotations"); OptOptions opts = new OptOptions(); IR ir = new IR(nm, null, opts); ir.MIRInfo = new MIRInfo(ir); ir.MIRInfo.regAllocState = state; SpillLocationManager spillLocManager = new SpillLocationManager(ir); this.spillLocations = spillLocManager; return new ActiveSet(ir, spillLocManager, null); } @Test public void activeSetStartsWithNothingSpilled() throws Exception { ActiveSet active = createActiveSet(); assertThat(active.spilledSomething(), is(false)); } @Test public void freeIntervalDeallocatesPhysicalRegisters() throws Exception { ActiveSet active = createActiveSet(); Register reg = new Register(FIRST_REG_NUMBER); Register target = new Register(SECOND_REG_NUMBER); reg.setPhysical(); reg.allocateRegister(target); CompoundInterval ci = new CompoundInterval(reg); MappedBasicInterval mapped = new MappedBasicInterval(new BasicInterval(1, 2), ci); active.freeInterval(mapped); assertThat(reg.isAllocated(), is(false)); assertNull(reg.mapsToRegister); } @Test public void freeIntervalMakesSpillIntervalsAvailableAgainWhenPossible() throws Exception { Register reg = new Register(FIRST_REG_NUMBER); reg.setInteger(); RegisterAllocatorState state = new RegisterAllocatorState(MAXIMUM_REGISTER_NUMBER); state.setSpill(reg, SPILL_LOCATION); ActiveSet active = createActiveSetFromRegisterAllocatorState(state); CompoundInterval ci = new CompoundInterval(reg); BasicInterval first = new BasicInterval(1, 2); MappedBasicInterval firstMapped = new MappedBasicInterval(first, ci); active.add(firstMapped); ci.add(firstMapped); ci.spill(spillLocations, state); SpillLocationInterval toBeFreed = ci.getSpillInterval(); active.freeInterval(firstMapped); assertThat(spillLocations.freeIntervals.contains(toBeFreed), is(true)); assertThat(spillLocations.freeIntervals.size(), is(1)); } @Test public void freeIntervalHasNoVisibleInfluenceOnFreeIntervalsWhenThereAreStillIntervalsLeftInTheCompoundInterval() throws Exception { Register reg = new Register(FIRST_REG_NUMBER); reg.setInteger(); RegisterAllocatorState state = new RegisterAllocatorState(MAXIMUM_REGISTER_NUMBER); state.setSpill(reg, SPILL_LOCATION); ActiveSet active = createActiveSetFromRegisterAllocatorState(state); CompoundInterval ci = new CompoundInterval(reg); BasicInterval first = new BasicInterval(1, 2); MappedBasicInterval firstMapped = new MappedBasicInterval(first, ci); active.add(firstMapped); ci.add(firstMapped); BasicInterval second = new BasicInterval(2, 3); MappedBasicInterval secondMapped = new MappedBasicInterval(second, ci); ci.add(secondMapped); ci.spill(spillLocations, state); assertThat(spillLocations.freeIntervals.size(), is(0)); active.freeInterval(firstMapped); assertThat(spillLocations.freeIntervals.size(), is(0)); } @Test public void freeIntervalWorksForIntervalsAssignedToUnspilledNonPhysicalRegisters() throws Exception { Register reg = new Register(FIRST_REG_NUMBER); Register mappedReg = new Register(SECOND_REG_NUMBER); reg.allocateRegister(mappedReg); mappedReg.setPhysical(); mappedReg.allocateRegister(); RegisterAllocatorState state = new RegisterAllocatorState(MAXIMUM_REGISTER_NUMBER); ActiveSet active = createActiveSetFromRegisterAllocatorState(state); CompoundInterval ci = new CompoundInterval(reg); BasicInterval first = new BasicInterval(1, 2); MappedBasicInterval firstMapped = new MappedBasicInterval(first, ci); active.add(firstMapped); ci.add(firstMapped); BasicInterval second = new BasicInterval(2, 3); MappedBasicInterval secondMapped = new MappedBasicInterval(second, ci); ci.add(secondMapped); active.freeInterval(firstMapped); assertThat(mappedReg.isAllocated(), is(false)); assertNull(mappedReg.mapsToRegister); } @Test public void currentlyActiveReturnsTrueForActiveRegisters() throws Exception { Register mapSource = new Register(FIRST_REG_NUMBER); Register mapTarget = new Register(SECOND_REG_NUMBER); BasicInterval bi = new BasicInterval(1, 2); CompoundInterval ci = new CompoundInterval(mapSource); MappedBasicInterval mbi = new MappedBasicInterval(bi, ci); RegisterAllocatorState state = new RegisterAllocatorState(30); state.mapOneToOne(mapSource, mapTarget); ActiveSet active = createActiveSetFromRegisterAllocatorState(state); active.add(mbi); assertThat(active.currentlyActive(mapTarget), is(true)); } @Test public void currentlyActiveReturnsFalseForEmptySets() throws Exception { Register unused = new Register(FIRST_REG_NUMBER); ActiveSet active = createActiveSet(); assertThat(active.currentlyActive(unused), is(false)); } @Test public void currentlyActiveReturnsFalseForNonactiveRegisters() throws Exception { Register unused = new Register(UNUSED_REG_NUMBER); Register mapSource = new Register(FIRST_REG_NUMBER); Register mapTarget = new Register(SECOND_REG_NUMBER); BasicInterval bi = new BasicInterval(1, 2); CompoundInterval ci = new CompoundInterval(mapSource); MappedBasicInterval mbi = new MappedBasicInterval(bi, ci); RegisterAllocatorState state = new RegisterAllocatorState(30); state.mapOneToOne(mapSource, mapTarget); ActiveSet active = createActiveSetFromRegisterAllocatorState(state); active.add(mbi); assertThat(active.currentlyActive(unused), is(false)); } @Test(expected = OptimizingCompilerException.class) public void getCurrentIntervalIsAssumedToAlwaysReturnAValidValue() throws Exception { Register unused = new Register(UNUSED_REG_NUMBER); ActiveSet active = createActiveSet(); active.getCurrentInterval(unused); } @Test public void getCurrentIntervalReturnsTheCorrectIntervalForActiveRegisters() throws Exception { Register mapSource = new Register(FIRST_REG_NUMBER); Register mapTarget = new Register(SECOND_REG_NUMBER); BasicInterval bi = new BasicInterval(1, 2); CompoundInterval ci = new CompoundInterval(mapSource); MappedBasicInterval mbi = new MappedBasicInterval(bi, ci); RegisterAllocatorState state = new RegisterAllocatorState(30); state.mapOneToOne(mapSource, mapTarget); ActiveSet active = createActiveSetFromRegisterAllocatorState(state); active.add(mbi); assertThat(active.getCurrentInterval(mapTarget), is(ci)); } @Test public void findAvailableRegisterIgnoresInfrequentIntervalsWhenDesired() throws Exception { NormalMethod nm = TestingTools.getNormalMethod(MethodsForTests.class, "emptyStaticMethodWithoutAnnotations"); OptOptions opts = new OptOptions(); opts.FREQ_FOCUS_EFFORT = true; IR ir = new IR(nm, null, opts); ir.MIRInfo = new MIRInfo(ir); SpillLocationManager spillLocManager = new SpillLocationManager(ir); this.spillLocations = spillLocManager; ActiveSet active = new ActiveSet(ir, spillLocManager, null); CompoundInterval infrequentCode = new CompoundInterval(null); assertNull(active.findAvailableRegister(infrequentCode)); } @Test public void getBasicIntervalReturnsNullIfNoBasicIntervalForTheRegisterContainsTheInstruction() throws Exception { RegisterAllocatorState state = new RegisterAllocatorState(1); ActiveSet active = createActiveSetFromRegisterAllocatorState(state); Instruction fence = Empty.create(FENCE); BasicInterval bi = active.getBasicInterval(new Register(0), fence); assertNull(bi); } @Test public void getBasicIntervalReturnsCorrectIntervalIfRegisterContainsTheInstruction() throws Exception { Register regZero = new Register(0); RegisterAllocatorState state = new RegisterAllocatorState(1); CompoundInterval ci = new CompoundInterval(regZero); MappedBasicInterval mbi = new MappedBasicInterval(1, 10, ci); ci.add(mbi); Instruction fence = Empty.create(FENCE); state.setInterval(regZero, ci); state.initializeDepthFirstNumbering(10); state.setDFN(fence, 5); ActiveSet active = createActiveSetFromRegisterAllocatorState(state); BasicInterval bi = active.getBasicInterval(regZero, fence); assertThat(bi.getBegin(), is(1)); assertThat(bi.getEnd(), is(10)); } @Test public void expireOldIntervalsRemovesIntervalsWhoseEndPrecedesTheNewStart() throws Exception { Register reg = new Register(FIRST_REG_NUMBER); reg.setInteger(); RegisterAllocatorState state = new RegisterAllocatorState(MAXIMUM_REGISTER_NUMBER); state.setSpill(reg, SPILL_LOCATION); ActiveSet active = createActiveSetFromRegisterAllocatorState(state); CompoundInterval ci = new CompoundInterval(reg); BasicInterval first = new BasicInterval(1, 2); BasicInterval firstMapped = new MappedBasicInterval(first, ci); active.add(firstMapped); ci.add(firstMapped); BasicInterval second = new BasicInterval(7, 8); BasicInterval secondMapped = new MappedBasicInterval(second, ci); active.add(secondMapped); ci.add(secondMapped); ci.spill(spillLocations, state); assertThat(active.size(), is(2)); active.expireOldIntervals(new MappedBasicInterval(new BasicInterval(4, 5), ci)); assertThat(active.size(), is(1)); } @Test public void expireOldIntervalsCallsFreeInterval() throws Exception { Register reg = new Register(FIRST_REG_NUMBER); reg.setInteger(); RegisterAllocatorState state = new RegisterAllocatorState(MAXIMUM_REGISTER_NUMBER); state.setSpill(reg, SPILL_LOCATION); ActiveSet active = createActiveSetFromRegisterAllocatorState(state); CompoundInterval ci = new CompoundInterval(reg); BasicInterval first = new BasicInterval(1, 2); BasicInterval firstMapped = new MappedBasicInterval(first, ci); active.add(firstMapped); ci.add(firstMapped); ci.spill(spillLocations, state); assertThat(active.size(), is(1)); active.expireOldIntervals(new MappedBasicInterval(new BasicInterval(4, 5), ci)); assertThat(active.size(), is(0)); assertThat(spillLocations.freeIntervals.size(), is(1)); } }