/* * 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.lir2mir; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.jikesrvm.compilers.opt.ir.Operators.FENCE; import static org.jikesrvm.compilers.opt.ir.Operators.INT_IFCMP; import static org.junit.Assert.assertThat; import java.util.Enumeration; import org.jikesrvm.compilers.opt.OptOptions; import org.jikesrvm.compilers.opt.OptimizingCompilerException; import org.jikesrvm.compilers.opt.ir.BasicBlock; import org.jikesrvm.compilers.opt.ir.Empty; import org.jikesrvm.compilers.opt.ir.IR; import org.jikesrvm.compilers.opt.ir.IfCmp; import org.jikesrvm.compilers.opt.ir.Instruction; import org.jikesrvm.junit.runners.RequiresBuiltJikesRVM; import org.jikesrvm.junit.runners.RequiresOptCompiler; import org.jikesrvm.junit.runners.VMRequirements; import org.jikesrvm.tests.util.TestingTools; import org.junit.Before; 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 SplitBasicBlockTest { private SplitBasicBlock splitPhase; @Before public void createPhase() { splitPhase = new SplitBasicBlock(); } @Test public void doesNotThrowExceptionsOnCFGWithoutInstructions() { int maxInstPerBlock = 0; IR ir = createIRWithEmptyCFG(maxInstPerBlock); splitPhase.perform(ir); } @Test public void doesNotAddInstructionsToEmptyCFG() { int maxInstPerBlock = 0; IR ir = createIRWithEmptyCFG(maxInstPerBlock); splitPhase.perform(ir); assertThatInstructionCountForEachBlockIsAtMost(ir, maxInstPerBlock); } @Test public void doesNotSplitEmptyCFG() { int maxInstPerBlock = 1; IR ir = createIRWithEmptyCFG(maxInstPerBlock); int nodeNumberBefore = ir.cfg.numberOfNodes(); splitPhase.perform(ir); int nodeNumberAfter = ir.cfg.numberOfNodes(); assertThat(nodeNumberAfter, is(nodeNumberBefore)); } private void assertThatInstructionCountForEachBlockIsAtMost(IR ir, int maxInstrPerBlock) { Enumeration<BasicBlock> blocks = ir.getBasicBlocks(); assertThat(blocks.hasMoreElements(), is(true)); while (blocks.hasMoreElements()) { BasicBlock block = blocks.nextElement(); // Note: This is slow but it's required to determine if the compiler phase // works correctly int numberOfRealInstructions = block.getNumberOfRealInstructions(); assertThat(numberOfRealInstructions, lessThanOrEqualTo(maxInstrPerBlock)); } } private IR createIRWithEmptyCFG(int maxInstPerBlock) { OptOptions opts = new OptOptions(); IR ir = new IR(null, null, opts); opts.L2M_MAX_BLOCK_SIZE = maxInstPerBlock; TestingTools.addEmptyCFGToIR(ir); return ir; } @Test public void doesNotCreateNewBlocksWhenNumberOfInstructionsMatchesLimit() { int maxInstPerBlock = 2; IR ir = createIRWithEmptyCFG(maxInstPerBlock); int nodeNumberBefore = ir.cfg.numberOfNodes(); addNumberOfInstructionsToBlock(ir, 2); splitPhase.perform(ir); int nodeNumberAfter = ir.cfg.numberOfNodes(); assertThat(nodeNumberAfter, is(nodeNumberBefore)); assertThatInstructionCountForEachBlockIsAtMost(ir, maxInstPerBlock); } @Test public void splitsAllBlocksThatHaveTooManyInstructions() { int maxInstPerBlock = 2; IR ir = createIRWithEmptyCFG(maxInstPerBlock); addNumberOfInstructionsToBlock(ir, 13); splitPhase.perform(ir); assertThatInstructionCountForEachBlockIsAtMost(ir, maxInstPerBlock); } @Test(timeout = 1000) public void worksCorrectlyForALimitOfOneInstructionPerBlock() { int maxInstPerBlock = 1; IR ir = createIRWithEmptyCFG(maxInstPerBlock); int nodeNumberBefore = ir.cfg.numberOfNodes(); addNumberOfInstructionsToBlock(ir, 1); splitPhase.perform(ir); int nodeNumberAfter = ir.cfg.numberOfNodes(); assertThat(nodeNumberAfter, is(nodeNumberBefore)); assertThatInstructionCountForEachBlockIsAtMost(ir, maxInstPerBlock); } private void addNumberOfInstructionsToBlock(IR ir, int count) { Enumeration<BasicBlock> blocks = ir.getBasicBlocks(); while (blocks.hasMoreElements()) { BasicBlock bb = blocks.nextElement(); for (int currInstCount = 0; currInstCount < count; currInstCount++) { bb.appendInstruction(Empty.create(FENCE)); } } } @Test(expected = OptimizingCompilerException.class) public void throwsOptimizingCompilerExceptionWhenLimitIsInvalid() { int maxInstPerBlock = 0; IR ir = createIRWithEmptyCFG(maxInstPerBlock); addNumberOfInstructionsToBlock(ir, 1); splitPhase.perform(ir); } @Test public void branchesAtTheEndOfTheBlockDoNotCountAsIntructions() { int maxInstPerBlock = 3; IR ir = createIRWithEmptyCFG(maxInstPerBlock); int nodeNumberBefore = ir.cfg.numberOfNodes(); Enumeration<BasicBlock> blocks = ir.getBasicBlocks(); while (blocks.hasMoreElements()) { BasicBlock bb = blocks.nextElement(); bb.appendInstruction(Empty.create(FENCE)); bb.appendInstruction(Empty.create(FENCE)); Instruction i = IfCmp.create(INT_IFCMP, null, null, null, null, null, null); bb.appendInstruction(i); } splitPhase.perform(ir); int nodeNumberAfter = ir.cfg.numberOfNodes(); assertThat(nodeNumberAfter, is(nodeNumberBefore)); assertThatInstructionCountForEachBlockIsAtMost(ir, maxInstPerBlock); } }