/*
* 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.bc2ir;
import static org.hamcrest.CoreMatchers.is;
import static org.jikesrvm.compilers.opt.driver.OptConstants.EPILOGUE_BCI;
import static org.jikesrvm.compilers.opt.driver.OptConstants.EPILOGUE_BLOCK_BCI;
import static org.jikesrvm.compilers.opt.driver.OptConstants.PROLOGUE_BCI;
import static org.jikesrvm.compilers.opt.driver.OptConstants.PROLOGUE_BLOCK_BCI;
import static org.jikesrvm.compilers.opt.driver.OptConstants.SYNCHRONIZED_MONITORENTER_BCI;
import static org.jikesrvm.compilers.opt.driver.OptConstants.SYNCHRONIZED_MONITOREXIT_BCI;
import static org.jikesrvm.compilers.opt.driver.OptConstants.SYNTH_CATCH_BCI;
import static org.jikesrvm.compilers.opt.driver.OptConstants.UNKNOWN_BCI;
import static org.jikesrvm.compilers.opt.ir.Operators.CALL;
import static org.jikesrvm.compilers.opt.ir.Operators.DOUBLE_MOVE;
import static org.jikesrvm.compilers.opt.ir.Operators.GET_CAUGHT_EXCEPTION;
import static org.jikesrvm.compilers.opt.ir.Operators.GUARD_MOVE;
import static org.jikesrvm.compilers.opt.ir.Operators.INT_MOVE;
import static org.jikesrvm.compilers.opt.ir.Operators.IR_PROLOGUE;
import static org.jikesrvm.compilers.opt.ir.Operators.LONG_MOVE;
import static org.jikesrvm.compilers.opt.ir.Operators.MONITORENTER;
import static org.jikesrvm.compilers.opt.ir.Operators.MONITOREXIT;
import static org.jikesrvm.compilers.opt.ir.Operators.OSR_BARRIER;
import static org.jikesrvm.compilers.opt.ir.Operators.REF_MOVE;
import static org.jikesrvm.compilers.opt.ir.Operators.RETURN;
import static org.jikesrvm.compilers.opt.ir.Operators.UNINT_BEGIN;
import static org.jikesrvm.compilers.opt.ir.Operators.UNINT_END;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import org.jikesrvm.VM;
import org.jikesrvm.classloader.NormalMethod;
import org.jikesrvm.classloader.RVMType;
import org.jikesrvm.classloader.TypeReference;
import org.jikesrvm.compilers.common.CompiledMethod;
import org.jikesrvm.compilers.opt.OptOptions;
import org.jikesrvm.compilers.opt.OptimizingCompilerException;
import org.jikesrvm.compilers.opt.inlining.DefaultInlineOracle;
import org.jikesrvm.compilers.opt.inlining.InlineOracle;
import org.jikesrvm.compilers.opt.inlining.InlineSequence;
import org.jikesrvm.compilers.opt.ir.BasicBlock;
import org.jikesrvm.compilers.opt.ir.Call;
import org.jikesrvm.compilers.opt.ir.Empty;
import org.jikesrvm.compilers.opt.ir.ExceptionHandlerBasicBlock;
import org.jikesrvm.compilers.opt.ir.ExceptionHandlerBasicBlockBag;
import org.jikesrvm.compilers.opt.ir.Instruction;
import org.jikesrvm.compilers.opt.ir.MonitorOp;
import org.jikesrvm.compilers.opt.ir.Move;
import org.jikesrvm.compilers.opt.ir.Nullary;
import org.jikesrvm.compilers.opt.ir.Operator;
import org.jikesrvm.compilers.opt.ir.OsrBarrier;
import org.jikesrvm.compilers.opt.ir.Prologue;
import org.jikesrvm.compilers.opt.ir.Register;
import org.jikesrvm.compilers.opt.ir.Return;
import org.jikesrvm.compilers.opt.ir.operand.ClassConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.MethodOperand;
import org.jikesrvm.compilers.opt.ir.operand.ObjectConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.Operand;
import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand;
import org.jikesrvm.compilers.opt.ir.operand.TrueGuardOperand;
import org.jikesrvm.compilers.opt.ir.operand.TypeOperand;
import org.jikesrvm.compilers.opt.runtimesupport.OptCompiledMethod;
import org.jikesrvm.compilers.opt.util.GraphEdge;
import org.jikesrvm.compilers.opt.util.SpaceEffGraphNode.OutEdgeEnumeration;
import org.jikesrvm.junit.runners.RequiresBuiltJikesRVM;
import org.jikesrvm.junit.runners.RequiresOptCompiler;
import org.jikesrvm.junit.runners.VMRequirements;
import org.jikesrvm.runtime.Entrypoints;
import org.jikesrvm.runtime.Statics;
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;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.Offset;
@RunWith(VMRequirements.class)
@Category({RequiresBuiltJikesRVM.class, RequiresOptCompiler.class})
public class GenerationContextTest {
private int currentRegisterNumber = -1;
@Test
public void constructorIsCorrectForTheSimplestMethods() throws Exception {
NormalMethod nm = getNormalMethodForTest("emptyStaticMethodWithoutAnnotations");
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = new OptOptions();
InlineOracle io = new DefaultInlineOracle();
GenerationContext gc = new GenerationContext(nm, null, cm, opts, io);
assertThatStateIsCorrectForUnsynchronizedEmptyStaticMethod(nm, cm, io, gc);
}
private void assertThatNumberOfParametersIs(GenerationContext gc, int numberParams) {
assertThat(gc.getArguments().length, is(numberParams));
}
private void assertThatDataIsSavedCorrectly(NormalMethod nm,
CompiledMethod cm, InlineOracle io, GenerationContext gc) {
assertThatInlinePlanWasSetCorrectly(io, gc);
assertThatOriginalMethodWasSetCorrectly(nm, gc);
assertThatOriginalCompiledMethodWasSetCorrectly(cm, gc);
}
private void assertThatReturnInstructionReturnsVoid(BasicBlock epilogue) {
assertNull(Return.getVal(epilogue.lastRealInstruction()));
}
private void assertThatRegisterPoolExists(GenerationContext gc) {
assertNotNull(gc.getTemps());
}
private void assertThatChecksWontBeSkipped(GenerationContext gc) {
assertThat(gc.noCheckStoreChecks(),is(false));
assertThat(gc.noBoundsChecks(),is(false));
assertThat(gc.noNullChecks(),is(false));
}
private void assertThatGCHasNoExceptionHandlers(GenerationContext gc) {
assertNull(gc.getEnclosingHandlers());
assertThat(gc.generatedExceptionHandlers(), is(false));
}
private void assertThatReturnValueIsVoid(GenerationContext gc) {
assertNull(gc.getResultReg());
}
private void assertThatOriginalCompiledMethodWasSetCorrectly(CompiledMethod cm,
GenerationContext gc) {
assertThat(gc.getOriginalCompiledMethod(), is(cm));
}
private void assertThatOriginalMethodWasSetCorrectly(NormalMethod nm,
GenerationContext gc) {
assertThat(gc.getOriginalMethod(), is(nm));
}
private void assertThatInlinePlanWasSetCorrectly(InlineOracle io,
GenerationContext gc) {
assertThat(gc.getInlinePlan(), is(io));
}
private void assertThatExitBlockIsSetCorrectly(GenerationContext gc) {
assertThat(gc.getExit(), is(gc.getCfg().exit()));
}
private void assertThatLastInstructionInEpilogueIsReturn(
GenerationContext gc, BasicBlock epilogue) {
assertThat(epilogue.lastRealInstruction().operator(), is(RETURN));
assertThat(epilogue.lastRealInstruction().getBytecodeIndex(), is(EPILOGUE_BCI));
assertThat(epilogue.getNormalOut().nextElement(), is(gc.getExit()));
}
private void assertThatEpilogueBlockIsSetupCorrectly(GenerationContext gc,
InlineSequence inlineSequence, BasicBlock epilogue) {
assertThat(gc.getCfg().lastInCodeOrder(), is(epilogue));
assertThat(epilogue.firstInstruction().getBytecodeIndex(), is(EPILOGUE_BLOCK_BCI));
assertTrue(epilogue.firstInstruction().position().equals(inlineSequence));
}
private void assertThatFirstInstructionInPrologueIsPrologue(
InlineSequence inlineSequence, BasicBlock prologue) {
assertThat(prologue.firstRealInstruction().getBytecodeIndex(),is(PROLOGUE_BCI));
assertTrue(prologue.firstRealInstruction().position().equals(inlineSequence));
assertThat(prologue.firstRealInstruction().operator(),is(IR_PROLOGUE));
}
private void assertThatPrologueBlockIsSetupCorrectly(GenerationContext gc,
InlineSequence inlineSequence, BasicBlock prologue) {
assertThat(gc.getCfg().firstInCodeOrder(), is(prologue));
assertThat(prologue.firstInstruction().getBytecodeIndex(), is(PROLOGUE_BLOCK_BCI));
assertTrue(prologue.firstInstruction().position().equals(inlineSequence));
}
private void assertThatInlineSequenceWasSetCorrectly(GenerationContext gc,
InlineSequence inlineSequence) {
assertThat(gc.getInlineSequence().equals(inlineSequence),is(true));
}
@Test
public void checkMethodsQueryUnderlyingMethod() throws Exception {
GenerationContext gc = createMostlyEmptyContext("emptyStaticMethodWithNoBoundCheckAnnotation");
assertThat(gc.noBoundsChecks(),is(true));
assertThat(gc.noCheckStoreChecks(),is(false));
assertThat(gc.noNullChecks(),is(false));
GenerationContext gc2 = createMostlyEmptyContext("emptyStaticMethodWithNoCheckStoreAnnotation");
assertThat(gc2.noBoundsChecks(),is(false));
assertThat(gc2.noCheckStoreChecks(),is(true));
assertThat(gc2.noNullChecks(),is(false));
GenerationContext gc3 = createMostlyEmptyContext("emptyStaticMethodWithNoNullCheckAnnotation");
assertThat(gc3.noBoundsChecks(),is(false));
assertThat(gc3.noCheckStoreChecks(),is(false));
assertThat(gc3.noNullChecks(),is(true));
}
@Test
public void instanceMethodsHaveMovesInPrologue() throws Exception {
NormalMethod nm = getNormalMethodForTest("emptyInstanceMethodWithoutAnnotations");
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = new OptOptions();
InlineOracle io = new DefaultInlineOracle();
GenerationContext gc = new GenerationContext(nm, null, cm, opts, io);
InlineSequence inlineSequence = new InlineSequence(nm);
assertThatInlineSequenceWasSetCorrectly(gc, inlineSequence);
assertThatLocalsForInstanceMethodWithoutParametersAreCorrect(nm, gc);
RegisterOperand thisOperand = getThisOperand(gc);
BasicBlock prologue = gc.getPrologue();
assertThatPrologueBlockIsSetupCorrectly(gc, inlineSequence, prologue);
assertThatFirstInstructionInPrologueIsPrologue(inlineSequence, prologue);
assertThatPrologueOperandIsRegOpFromLocalNr(prologue, thisOperand, 0);
Instruction lastPrologueInstruction = prologue.lastRealInstruction();
assertThatInstructionIsGuardMoveForPrologue(lastPrologueInstruction, thisOperand, gc);
assertThatEpilogueIsForVoidReturnMethod(gc, inlineSequence);
assertThatExitBlockIsSetCorrectly(gc);
assertThatDataIsSavedCorrectly(nm, cm, io, gc);
assertThatReturnValueIsVoid(gc);
assertThatGCHasNoExceptionHandlers(gc);
assertThatRegisterPoolExists(gc);
assertThatChecksWontBeSkipped(gc);
assertThatNoRethrowBlockExists(gc);
}
private void assertThatBlockOnlyHasOneRealInstruction(BasicBlock block) {
assertThat(block.lastRealInstruction(),is(block.firstRealInstruction()));
}
@Test
public void methodsWithReturnValueHaveReturnInstructionInEpilogue() throws Exception {
NormalMethod nm = getNormalMethodForTest("staticMethodReturningLongMaxValue");
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = new OptOptions();
InlineOracle io = new DefaultInlineOracle();
GenerationContext gc = new GenerationContext(nm, null, cm, opts, io);
InlineSequence inlineSequence = new InlineSequence(nm);
assertThatInlineSequenceWasSetCorrectly(gc, inlineSequence);
assertThatNumberOfParametersIs(gc, 0);
BasicBlock prologue = gc.getPrologue();
assertThatPrologueBlockIsSetupCorrectly(gc, inlineSequence, prologue);
assertThatFirstInstructionInPrologueIsPrologue(inlineSequence, prologue);
assertThatBlockOnlyHasOneRealInstruction(prologue);
BasicBlock epilogue = gc.getEpilogue();
assertThatEpilogueBlockIsSetupCorrectly(gc, inlineSequence, epilogue);
assertThatLastInstructionInEpilogueIsReturn(gc, epilogue);
RegisterOperand returnValue = Return.getVal(epilogue.lastRealInstruction()).asRegister();
assertThat(returnValue.getType(),is(TypeReference.Long));
assertThatExitBlockIsSetCorrectly(gc);
assertThatDataIsSavedCorrectly(nm, cm, io, gc);
assertThat(gc.getResultReg().isLong(),is(true));
assertThatGCHasNoExceptionHandlers(gc);
assertThatRegisterPoolExists(gc);
assertThatChecksWontBeSkipped(gc);
}
private static GenerationContext createMostlyEmptyContext(String methodName) throws Exception {
NormalMethod nm = TestingTools.getNormalMethod(MethodsForTests.class, methodName);
OptOptions opts = new OptOptions();
GenerationContext gc = new GenerationContext(nm, null, null, opts, null);
return gc;
}
@Test
public void constructorCreatesOperandsForStaticMethodWithParameters() throws Exception {
Class<?>[] argumentTypes = {long.class, int.class, Object.class, double.class};
NormalMethod nm = getNormalMethodForTest("emptyStaticMethodWithParams", argumentTypes);
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = new OptOptions();
InlineOracle io = new DefaultInlineOracle();
GenerationContext gc = new GenerationContext(nm, null, cm, opts, io);
InlineSequence inlineSequence = new InlineSequence(nm);
assertThatInlineSequenceWasSetCorrectly(gc, inlineSequence);
assertThatNumberOfParametersIs(gc, 4);
RegisterOperand longRegOp = getThisOperand(gc);
assertThat(longRegOp.isLong(),is(true));
assertThatRegOpHasDeclaredType(longRegOp);
RegisterOperand intRegOp = gc.getArguments()[1].asRegister();
assertThat(intRegOp.isInt(), is(true));
assertThatRegOpHasDeclaredType(intRegOp);
RegisterOperand objectRegOp = gc.getArguments()[2].asRegister();
assertThatRegOpHoldsClassType(objectRegOp);
assertThatRegOpHasDeclaredType(objectRegOp);
RegisterOperand doubleRegOp = gc.getArguments()[3].asRegister();
assertThat(doubleRegOp.isDouble(), is(true));
assertThatRegOpHasDeclaredType(doubleRegOp);
Register longReg = gc.localReg(0, TypeReference.Long);
assertThatRegOpIsLocalRegOfRegister(longRegOp, longReg);
Register intReg = gc.localReg(2, TypeReference.Int);
assertThatRegOpIsLocalRegOfRegister(intRegOp, intReg);
Register objectReg = gc.localReg(3, TypeReference.JavaLangObject);
assertThatRegOpIsLocalRegOfRegister(objectRegOp, objectReg);
Register doubleReg = gc.localReg(4, TypeReference.Double);
assertThatRegOpIsLocalRegOfRegister(doubleRegOp, doubleReg);
BasicBlock prologue = gc.getPrologue();
assertThatPrologueBlockIsSetupCorrectly(gc, inlineSequence, prologue);
assertThatFirstInstructionInPrologueIsPrologue(inlineSequence, prologue);
assertThatBlockOnlyHasOneRealInstruction(prologue);
assertThatPrologueOperandIsRegOpFromLocalNr(prologue, longRegOp, 0);
assertThatPrologueOperandIsRegOpFromLocalNr(prologue, intRegOp, 1);
assertThatPrologueOperandIsRegOpFromLocalNr(prologue, objectRegOp, 2);
assertThatPrologueOperandIsRegOpFromLocalNr(prologue, doubleRegOp, 3);
assertThatEpilogueIsForVoidReturnMethod(gc, inlineSequence);
assertThatExitBlockIsSetCorrectly(gc);
assertThatDataIsSavedCorrectly(nm, cm, io, gc);
assertThatReturnValueIsVoid(gc);
assertThatGCHasNoExceptionHandlers(gc);
assertThatRegisterPoolExists(gc);
assertThatChecksWontBeSkipped(gc);
assertThatNoRethrowBlockExists(gc);
}
private void assertThatPrologueOperandIsRegOpFromLocalNr(BasicBlock prologue,
RegisterOperand regOp, int localNr) {
RegisterOperand prologueOp = Prologue.getFormal(prologue.firstRealInstruction(),localNr);
assertThat(prologueOp.sameRegisterPropertiesAs(regOp),is(true));
}
private void assertThatRegOpIsLocalRegOfRegister(RegisterOperand regOp,
Register reg) {
assertThat(regOp.getRegister(), is(reg));
assertThat(reg.isLocal(), is(true));
}
private void assertThatRegOpHoldsClassType(RegisterOperand objectRegOp) {
assertThat(objectRegOp.isRef(), is(true));
assertThat(objectRegOp.isExtant(), is(true));
}
private void assertThatRegOpHasDeclaredType(RegisterOperand regOp) {
assertThat(regOp.isDeclaredType(),is(true));
}
@Test
public void constructorCreatesOperandsForInstanceMethods() throws Exception {
Class<?>[] argumentTypes = {Object.class, double.class, int.class, long.class};
NormalMethod nm = getNormalMethodForTest("emptyInstanceMethodWithParams", argumentTypes);
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = new OptOptions();
InlineOracle io = new DefaultInlineOracle();
GenerationContext gc = new GenerationContext(nm, null, cm, opts, io);
InlineSequence inlineSequence = new InlineSequence(nm);
assertThatInlineSequenceWasSetCorrectly(gc, inlineSequence);
assertThatNumberOfParametersIs(gc, 5);
RegisterOperand thisOperand = getThisOperand(gc);
assertThatRegOpHoldsClassType(thisOperand);
assertThatRegOpHasDeclaredType(thisOperand);
RegisterOperand objectRegOp = gc.getArguments()[1].asRegister();
assertThatRegOpHoldsClassType(objectRegOp);
assertThatRegOpHasDeclaredType(objectRegOp);
RegisterOperand doubleRegOp = gc.getArguments()[2].asRegister();
assertThat(doubleRegOp.isDouble(), is(true));
assertThatRegOpHasDeclaredType(doubleRegOp);
RegisterOperand intRegOp = gc.getArguments()[3].asRegister();
assertThat(intRegOp.isInt(), is(true));
assertThatRegOpHasDeclaredType(intRegOp);
RegisterOperand longRegOp = gc.getArguments()[4].asRegister();
assertThat(longRegOp.isLong(),is(true));
assertThatRegOpHasDeclaredType(longRegOp);
Register thisLocalReg = gc.localReg(0, nm.getDeclaringClass().getTypeRef());
assertThatRegOpIsLocalRegOfRegister(thisOperand, thisLocalReg);
Register objectReg = gc.localReg(1, TypeReference.JavaLangObject);
assertThatRegOpIsLocalRegOfRegister(objectRegOp, objectReg);
Register doubleReg = gc.localReg(2, TypeReference.Double);
assertThatRegOpIsLocalRegOfRegister(doubleRegOp, doubleReg);
Register intReg = gc.localReg(4, TypeReference.Int);
assertThatRegOpIsLocalRegOfRegister(intRegOp, intReg);
Register longReg = gc.localReg(5, TypeReference.Long);
assertThatRegOpIsLocalRegOfRegister(longRegOp, longReg);
BasicBlock prologue = gc.getPrologue();
assertThatPrologueBlockIsSetupCorrectly(gc, inlineSequence, prologue);
assertThatFirstInstructionInPrologueIsPrologue(inlineSequence, prologue);
Instruction lastPrologueInstruction = prologue.lastRealInstruction();
assertThatInstructionIsGuardMoveForPrologue(lastPrologueInstruction, thisOperand, gc);
assertThatPrologueOperandIsRegOpFromLocalNr(prologue, thisOperand, 0);
assertThatPrologueOperandIsRegOpFromLocalNr(prologue, objectRegOp, 1);
assertThatPrologueOperandIsRegOpFromLocalNr(prologue, doubleRegOp, 2);
assertThatPrologueOperandIsRegOpFromLocalNr(prologue, intRegOp, 3);
assertThatPrologueOperandIsRegOpFromLocalNr(prologue, longRegOp, 4);
assertThatEpilogueIsForVoidReturnMethod(gc, inlineSequence);
assertThatExitBlockIsSetCorrectly(gc);
assertThatDataIsSavedCorrectly(nm, cm, io, gc);
assertThatReturnValueIsVoid(gc);
assertThatGCHasNoExceptionHandlers(gc);
assertThatRegisterPoolExists(gc);
assertThatChecksWontBeSkipped(gc);
assertThatNoRethrowBlockExists(gc);
}
private void assertThatInstructionIsGuardMoveForPrologue(Instruction instruction, RegisterOperand thisOp, GenerationContext gc) {
assertThat(instruction.operator(),is(GUARD_MOVE));
assertThat(instruction.getBytecodeIndex(),is(PROLOGUE_BCI));
assertTrue(Move.getVal(instruction).isTrueGuard());
Operand o = Move.getResult(instruction);
assertTrue(o.isRegister());
RegisterOperand val = o.asRegister();
RegisterOperand nullCheckGuard = gc.makeNullCheckGuard(thisOp.getRegister());
assertTrue(val.sameRegisterPropertiesAs(nullCheckGuard));
}
@Test
public void specializationOfParametersIsSupported() throws Exception {
Class<?>[] argumentTypes = {Object.class};
TypeReference typeRef = JikesRVMSupport.getTypeForClass(Uninterruptible.class).getTypeRef();
TypeReference[] specializedArgumentTypes = {typeRef};
NormalMethod nm = getNormalMethodForTest("emptyStaticMethodWithObjectParam", argumentTypes);
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = new OptOptions();
InlineOracle io = new DefaultInlineOracle();
GenerationContext gc = new GenerationContext(nm, specializedArgumentTypes, cm, opts, io);
InlineSequence inlineSequence = new InlineSequence(nm);
assertThatInlineSequenceWasSetCorrectly(gc, inlineSequence);
assertThatNumberOfParametersIs(gc, 1);
RegisterOperand objectRegOp = getThisOperand(gc);
assertThatRegOpHoldsClassType(objectRegOp);
assertThatRegOpHasDeclaredType(objectRegOp);
Register objectReg = gc.localReg(0, TypeReference.Uninterruptible);
assertThatRegOpIsLocalRegOfRegister(objectRegOp, objectReg);
BasicBlock prologue = gc.getPrologue();
assertThatPrologueBlockIsSetupCorrectly(gc, inlineSequence, prologue);
assertThatFirstInstructionInPrologueIsPrologue(inlineSequence, prologue);
assertThatBlockOnlyHasOneRealInstruction(prologue);
assertThatPrologueOperandIsRegOpFromLocalNr(prologue, objectRegOp, 0);
assertThatEpilogueIsForVoidReturnMethod(gc, inlineSequence);
assertThatExitBlockIsSetCorrectly(gc);
assertThatDataIsSavedCorrectly(nm, cm, io, gc);
assertThatReturnValueIsVoid(gc);
assertThatGCHasNoExceptionHandlers(gc);
assertThatRegisterPoolExists(gc);
assertThatChecksWontBeSkipped(gc);
}
private void assertThatEpilogueIsForVoidReturnMethod(GenerationContext gc,
InlineSequence inlineSequence) {
BasicBlock epilogue = gc.getEpilogue();
assertThatEpilogueBlockIsSetupCorrectly(gc, inlineSequence, epilogue);
assertThatLastInstructionInEpilogueIsReturn(gc, epilogue);
assertThatReturnInstructionReturnsVoid(epilogue);
assertThatBlockOnlyHasOneRealInstruction(epilogue);
}
@Test
public void specializedParametersAreNotSanityChecked() throws Exception {
Class<?>[] argumentTypes = {Object.class};
TypeReference[] specializedArgumentTypes = {};
NormalMethod nm = getNormalMethodForTest("emptyStaticMethodWithObjectParam", argumentTypes);
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = new OptOptions();
InlineOracle io = new DefaultInlineOracle();
GenerationContext gc = new GenerationContext(nm, specializedArgumentTypes, cm, opts, io);
assertThatNumberOfParametersIs(gc, 0);
}
@Test
public void prologueAndEpilogueForSynchronizedStaticMethodHaveMonitorEnterAndExit() throws Exception {
NormalMethod nm = getNormalMethodForTest("emptySynchronizedStaticMethod");
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = new OptOptions();
InlineOracle io = new DefaultInlineOracle();
GenerationContext gc = new GenerationContext(nm, null, cm, opts, io);
InlineSequence inlineSequence = new InlineSequence(nm);
assertThatInlineSequenceWasSetCorrectly(gc, inlineSequence);
assertThatNumberOfParametersIs(gc, 0);
BasicBlock prologue = gc.getPrologue();
assertThatPrologueBlockIsSetupCorrectly(gc, inlineSequence, prologue);
assertThatFirstInstructionInPrologueIsPrologue(inlineSequence, prologue);
Enumeration<Instruction> prologueInstructions = prologue.forwardRealInstrEnumerator();
prologueInstructions.nextElement();
Instruction secondInstruction = prologueInstructions.nextElement();
assertInstructionIsMonitorEnterInPrologue(secondInstruction, inlineSequence);
assertThatGuardIsCorrectForMonitorEnter(secondInstruction);
Operand lockObject = MonitorOp.getRef(secondInstruction);
Operand expectedLockObject = buildLockObjectForStaticMethod(nm);
assertTrue(expectedLockObject.similar(lockObject));
assertThatNumberOfRealInstructionsMatches(prologue, 2);
BasicBlock epilogue = gc.getEpilogue();
assertThatEpilogueBlockIsSetupCorrectly(gc, inlineSequence, epilogue);
assertThatFirstInstructionEpilogueIsMonitorExit(inlineSequence, epilogue);
assertThatLastInstructionInEpilogueIsReturn(gc, epilogue);
assertThatReturnInstructionReturnsVoid(epilogue);
assertThatNumberOfRealInstructionsMatches(epilogue, 2);
assertThatExitBlockIsSetCorrectly(gc);
assertThatRegisterPoolExists(gc);
assertThatDataIsSavedCorrectly(nm, cm, io, gc);
assertThatReturnValueIsVoid(gc);
assertThatExceptionHandlersWereGenerated(gc);
assertThatUnlockAndRethrowBlockIsCorrect(gc, inlineSequence, prologue,
expectedLockObject, epilogue);
assertThatChecksWontBeSkipped(gc);
}
private void checkCodeOrderForRethrowBlock(GenerationContext gc,
BasicBlock prologue, BasicBlock epilogue,
ExceptionHandlerBasicBlock rethrow) {
BasicBlock firstBlockInCodeOrder = gc.getCfg().firstInCodeOrder();
assertTrue(firstBlockInCodeOrder.equals(prologue));
BasicBlock secondBlockInCodeOrder = firstBlockInCodeOrder.nextBasicBlockInCodeOrder();
assertTrue(secondBlockInCodeOrder.equals(rethrow));
BasicBlock lastBlockInCodeOrder = secondBlockInCodeOrder.nextBasicBlockInCodeOrder();
assertTrue(lastBlockInCodeOrder.equals(epilogue));
assertNull(lastBlockInCodeOrder.nextBasicBlockInCodeOrder());
}
private Operand buildLockObjectForStaticMethod(NormalMethod nm) {
Class<?> klass = nm.getDeclaringClass().getClassForType();
Offset offs = Offset.fromIntSignExtend(Statics.findOrCreateObjectLiteral(klass));
Operand expectedLockObject = new ClassConstantOperand(klass, offs);
return expectedLockObject;
}
private void assertThatNumberOfRealInstructionsMatches(BasicBlock b,
int expected) {
assertThat(b.getNumberOfRealInstructions(), is(expected));
}
@Test
public void prologueAndEpilogueForSynchronizedInstanceMethodHaveMonitorEnterAndExit() throws Exception {
NormalMethod nm = getNormalMethodForTest("emptySynchronizedInstanceMethod");
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = new OptOptions();
InlineOracle io = new DefaultInlineOracle();
GenerationContext gc = new GenerationContext(nm, null, cm, opts, io);
InlineSequence inlineSequence = new InlineSequence(nm);
assertThatInlineSequenceWasSetCorrectly(gc, inlineSequence);
assertThatLocalsForInstanceMethodWithoutParametersAreCorrect(nm, gc);
RegisterOperand thisOperand = getThisOperand(gc);
BasicBlock prologue = gc.getPrologue();
assertThatPrologueBlockIsSetupCorrectly(gc, inlineSequence, prologue);
assertThatFirstInstructionInPrologueIsPrologue(inlineSequence, prologue);
Enumeration<Instruction> prologueInstructions = prologue.forwardRealInstrEnumerator();
prologueInstructions.nextElement();
Instruction secondInstruction = prologueInstructions.nextElement();
assertThatInstructionIsGuardMoveForPrologue(secondInstruction, thisOperand, gc);
Instruction lastInstruction = prologueInstructions.nextElement();
assertInstructionIsMonitorEnterInPrologue(lastInstruction, inlineSequence);
assertThatGuardIsCorrectForMonitorEnter(lastInstruction);
Operand lockObject = MonitorOp.getRef(lastInstruction);
Operand expectedLockObject = buildLockObjectForInstanceMethod(nm, thisOperand, gc);
assertTrue(expectedLockObject.similar(lockObject));
assertThatNumberOfRealInstructionsMatches(prologue, 3);
BasicBlock epilogue = gc.getEpilogue();
assertThatEpilogueBlockIsSetupCorrectly(gc, inlineSequence, epilogue);
assertThatFirstInstructionEpilogueIsMonitorExit(inlineSequence, epilogue);
assertThatLastInstructionInEpilogueIsReturn(gc, epilogue);
assertThatReturnInstructionReturnsVoid(epilogue);
assertThatNumberOfRealInstructionsMatches(epilogue, 2);
assertThatExitBlockIsSetCorrectly(gc);
assertThatRegisterPoolExists(gc);
assertThatDataIsSavedCorrectly(nm, cm, io, gc);
assertThatReturnValueIsVoid(gc);
assertThatExceptionHandlersWereGenerated(gc);
assertThatUnlockAndRethrowBlockIsCorrect(gc, inlineSequence, prologue,
expectedLockObject, epilogue);
assertThatChecksWontBeSkipped(gc);
}
private void assertThatUnlockAndRethrowBlockIsCorrect(GenerationContext gc,
InlineSequence inlineSequence, BasicBlock prologue, Operand lockObject,
BasicBlock epilogue) {
ExceptionHandlerBasicBlockBag ehbb = gc.getEnclosingHandlers();
Enumeration<BasicBlock> enumerator = ehbb.enumerator();
ExceptionHandlerBasicBlock rethrow = (ExceptionHandlerBasicBlock) enumerator.nextElement();
assertThat(enumerator.hasMoreElements(), is(false));
assertThatRethrowBlockIsCorrect(inlineSequence, lockObject, rethrow);
checkCodeOrderForRethrowBlock(gc, prologue, epilogue, rethrow);
assertTrue(rethrow.mayThrowUncaughtException());
assertTrue(rethrow.canThrowExceptions());
OutEdgeEnumeration outEdges = rethrow.outEdges();
boolean leadsToExit = false;
while (outEdges.hasMoreElements()) {
GraphEdge nextElement = outEdges.nextElement();
BasicBlock target = (BasicBlock) nextElement.to();
if (target == gc.getCfg().exit()) {
leadsToExit = true;
}
}
assertTrue(leadsToExit);
assertSame(rethrow, gc.getUnlockAndRethrow());
ExceptionHandlerBasicBlockBag ehbbb = gc.getEnclosingHandlers();
assertNull(ehbbb.getCaller());
assertThatEnclosingHandlersContainRethrow(rethrow, ehbbb);
}
private void assertThatExceptionHandlersWereGenerated(GenerationContext gc) {
assertThat(gc.generatedExceptionHandlers(), is(true));
}
private void assertThatFirstInstructionEpilogueIsMonitorExit(
InlineSequence inlineSequence, BasicBlock epilogue) {
Instruction firstEpilogueInstruction = epilogue.firstRealInstruction();
assertThat(firstEpilogueInstruction.getBytecodeIndex(),is(SYNCHRONIZED_MONITOREXIT_BCI));
assertTrue(firstEpilogueInstruction.position().equals(inlineSequence));
assertThat(firstEpilogueInstruction.operator(),is(MONITOREXIT));
}
private void assertThatGuardIsCorrectForMonitorEnter(
Instruction inst) {
Operand guard = MonitorOp.getGuard(inst);
assertTrue(guard.similar(new TrueGuardOperand()));
}
private RegisterOperand buildLockObjectForInstanceMethod(NormalMethod nm,
RegisterOperand thisOp, GenerationContext gc) {
return gc.makeLocal(0, thisOp.getType());
}
private RegisterOperand getThisOperand(GenerationContext gc) {
return gc.getArguments()[0].asRegister();
}
private void assertThatLocalsForInstanceMethodWithoutParametersAreCorrect(NormalMethod nm,
GenerationContext gc) {
assertThatNumberOfParametersIs(gc, 1);
RegisterOperand thisOperand = getThisOperand(gc);
assertThatRegOpHoldsClassType(thisOperand);
assertThatRegOpHasDeclaredType(thisOperand);
Register thisLocalReg = gc.localReg(0, nm.getDeclaringClass().getTypeRef());
assertThatRegOpIsLocalRegOfRegister(thisOperand, thisLocalReg);
}
private void assertInstructionIsMonitorEnterInPrologue(
Instruction secondInstruction, InlineSequence inlineSequence) {
assertThat(secondInstruction.operator(),is(MONITORENTER));
assertTrue(secondInstruction.position().equals(inlineSequence));
assertThat(secondInstruction.getBytecodeIndex(),is(SYNCHRONIZED_MONITORENTER_BCI));
}
private NormalMethod getNormalMethodForTest(String methodName) throws Exception {
NormalMethod nm = TestingTools.getNormalMethod(MethodsForTests.class, methodName);
return nm;
}
private NormalMethod getNormalMethodForTest(String methodName, Class<?>[] argumentTypes) throws Exception {
NormalMethod nm = TestingTools.getNormalMethod(MethodsForTests.class, methodName, argumentTypes);
return nm;
}
@Test
public void threadLocalSynchronizedStaticMethodDoesNotHaveMonitorEnterAndMonitorExit() throws Exception {
NormalMethod nm = getNormalMethodForTest("emptySynchronizedStaticMethod");
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = new OptOptions();
opts.ESCAPE_INVOKEE_THREAD_LOCAL = true;
InlineOracle io = new DefaultInlineOracle();
GenerationContext gc = new GenerationContext(nm, null, cm, opts, io);
assertThatStateIsCorrectForUnsynchronizedEmptyStaticMethod(nm, cm, io, gc);
}
private void assertThatStateIsCorrectForUnsynchronizedEmptyStaticMethod(
NormalMethod nm, CompiledMethod cm, InlineOracle io, GenerationContext gc) {
InlineSequence inlineSequence = new InlineSequence(nm);
assertThatInlineSequenceWasSetCorrectly(gc, inlineSequence);
assertThatNumberOfParametersIs(gc, 0);
BasicBlock prologue = gc.getPrologue();
assertThatPrologueBlockIsSetupCorrectly(gc, inlineSequence, prologue);
assertThatFirstInstructionInPrologueIsPrologue(inlineSequence, prologue);
assertThatBlockOnlyHasOneRealInstruction(prologue);
assertThatEpilogueIsForVoidReturnMethod(gc, inlineSequence);
assertThatExitBlockIsSetCorrectly(gc);
assertThatRegisterPoolExists(gc);
assertThatDataIsSavedCorrectly(nm, cm, io, gc);
assertThatReturnValueIsVoid(gc);
assertThatGCHasNoExceptionHandlers(gc);
assertThatChecksWontBeSkipped(gc);
assertThatNoRethrowBlockExists(gc);
}
@Test
public void osrSpecializedMethodsDoNotHaveMonitorEnterButHaveMonitorExit() throws Exception {
NormalMethod nm = getNormalMethodForTest("emptySynchronizedStaticMethodForOSR");
byte[] osrPrologue = {};
nm.setForOsrSpecialization(osrPrologue, (short) 0);
assertTrue(nm.isForOsrSpecialization());
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = new OptOptions();
InlineOracle io = new DefaultInlineOracle();
GenerationContext gc = new GenerationContext(nm, null, cm, opts, io);
InlineSequence inlineSequence = new InlineSequence(nm);
assertThatInlineSequenceWasSetCorrectly(gc, inlineSequence);
assertThatNumberOfParametersIs(gc, 0);
BasicBlock prologue = gc.getPrologue();
assertThatPrologueBlockIsSetupCorrectly(gc, inlineSequence, prologue);
assertThatFirstInstructionInPrologueIsPrologue(inlineSequence, prologue);
assertThatNumberOfRealInstructionsMatches(prologue, 1);
BasicBlock epilogue = gc.getEpilogue();
assertThatEpilogueBlockIsSetupCorrectly(gc, inlineSequence, epilogue);
assertThatFirstInstructionEpilogueIsMonitorExit(inlineSequence, epilogue);
assertThatLastInstructionInEpilogueIsReturn(gc, epilogue);
assertThatReturnInstructionReturnsVoid(epilogue);
assertThatNumberOfRealInstructionsMatches(epilogue, 2);
assertThatExitBlockIsSetCorrectly(gc);
assertThatRegisterPoolExists(gc);
assertThatDataIsSavedCorrectly(nm, cm, io, gc);
assertThatReturnValueIsVoid(gc);
assertThatExceptionHandlersWereGenerated(gc);
Operand expectedLockObject = buildLockObjectForStaticMethod(nm);
assertThatUnlockAndRethrowBlockIsCorrect(gc, inlineSequence, prologue,
expectedLockObject, epilogue);
assertThatChecksWontBeSkipped(gc);
}
private RegisterOperand createMockRegisterOperand(TypeReference tr) {
Register r = new Register(currentRegisterNumber--);
return new RegisterOperand(r, tr);
}
@Test
public void basicChildContextsWorkCorrectly() throws Exception {
NormalMethod nm = getNormalMethodForTest("methodForInliningTests");
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = new OptOptions();
InlineOracle io = new DefaultInlineOracle();
GenerationContext gc = new GenerationContext(nm, null, cm, opts, io);
Class<?>[] classArgs = {Object.class};
NormalMethod callee = getNormalMethodForTest("emptyStaticMethodWithObjectParamAndReturnValue", classArgs);
MethodOperand methOp = MethodOperand.STATIC(callee);
RegisterOperand result = createMockRegisterOperand(TypeReference.JavaLangObject);
Instruction callInstr = Call.create(CALL, result, null, methOp, 1);
RegisterOperand objectParam = createMockRegisterOperand(TypeReference.JavaLangObject);
Call.setParam(callInstr, 0, objectParam);
callInstr.setPosition(new InlineSequence(nm));
ExceptionHandlerBasicBlockBag ebag = getMockEbag();
int nodeNumber = 12345;
gc.getCfg().setNumberOfNodes(nodeNumber);
GenerationContext child = gc.createChildContext(ebag, callee, callInstr);
RegisterOperand expectedLocalForObjectParam = child.makeLocal(0, objectParam);
assertThatStateIsCopiedFromParentToChild(gc, callee, child, ebag);
InlineSequence expectedInlineSequence = new InlineSequence(callee, callInstr.position(), callInstr);
assertEquals(expectedInlineSequence, child.getInlineSequence());
RegisterOperand firstArg = child.getArguments()[0].asRegister();
assertTrue(firstArg.sameRegisterPropertiesAs(expectedLocalForObjectParam));
assertSame(result.getRegister(), child.getResultReg());
assertTrue(child.getResultReg().spansBasicBlock());
assertThatPrologueAndEpilogueAreWiredCorrectlyForChildContext(ebag, nodeNumber, child);
Enumeration<Instruction> prologueRealInstr = child.getPrologue().forwardRealInstrEnumerator();
Instruction move = prologueRealInstr.nextElement();
RegisterOperand objectParamInChild = objectParam.copy().asRegister();
assertMoveOperationIsCorrect(callInstr, REF_MOVE,
expectedLocalForObjectParam, objectParamInChild, move);
assertThatNoMoreInstructionsExist(prologueRealInstr);
Enumeration<Instruction> epilogueRealInstr = child.getEpilogue().forwardRealInstrEnumerator();
assertThatNoMoreInstructionsExist(epilogueRealInstr);
assertThatNoRethrowBlockExists(child);
assertThatChecksWontBeSkipped(gc);
}
private void assertMoveOperationIsCorrect(Instruction call,
Operator moveOperator, RegisterOperand formal, RegisterOperand actual, Instruction move) {
assertSame(call.position(), move.position());
assertThat(move.operator(), is(moveOperator));
assertThat(move.getBytecodeIndex(), is(PROLOGUE_BCI));
RegisterOperand moveResult = Move.getResult(move);
assertTrue(moveResult.sameRegisterPropertiesAs(formal));
RegisterOperand moveValue = Move.getVal(move).asRegister();
assertTrue(moveValue.sameRegisterPropertiesAs(actual));
}
private void assertThatStateIsCopiedFromParentToChild(
GenerationContext parent, NormalMethod callee, GenerationContext child, ExceptionHandlerBasicBlockBag ebag) {
assertThat(child.getMethod(), is(callee));
assertThat(child.getOriginalMethod(), is(parent.getOriginalMethod()));
assertThat(child.getOriginalCompiledMethod(), is(parent.getOriginalCompiledMethod()));
assertThat(child.getOptions(), is(parent.getOptions()));
assertThat(child.getTemps(), is(parent.getTemps()));
assertThat(child.getExit(), is(parent.getExit()));
assertThat(child.getInlinePlan(), is(parent.getInlinePlan()));
assertThat(child.getEnclosingHandlers(), is(ebag));
}
@Test
public void getOriginalMethodReturnsTheOutermostMethod() throws Exception {
GenerationContext outermostContext = createMostlyEmptyContext("emptyStaticMethodWithNoBoundCheckAnnotation");
NormalMethod outermostCaller = getNormalMethodForTest("emptyStaticMethodWithNoBoundCheckAnnotation");
ExceptionHandlerBasicBlockBag ebag = getMockEbag();
NormalMethod callee = getNormalMethodForTest("emptyStaticMethodWithNoCheckStoreAnnotation");
Instruction noCheckStoreInstr = buildCallInstructionForStaticMethodWithoutReturn(callee, outermostCaller);
GenerationContext nextInnerContext = outermostContext.createChildContext(ebag, callee, noCheckStoreInstr);
assertThat(nextInnerContext.getOriginalMethod(), is(outermostCaller));
NormalMethod nextInnerCallee = getNormalMethodForTest("emptyStaticMethodWithNoNullCheckAnnotation");
Instruction noNullCheckInstr = buildCallInstructionForStaticMethodWithoutReturn(callee, nextInnerCallee);
GenerationContext innermostContext = nextInnerContext.createChildContext(ebag, nextInnerCallee, noNullCheckInstr);
assertThat(innermostContext.getOriginalMethod(), is(outermostCaller));
}
@Test
public void inliningInstanceMethodWithRegisterReceiver() throws Exception {
NormalMethod nm = getNormalMethodForTest("methodForInliningTests");
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = new OptOptions();
InlineOracle io = new DefaultInlineOracle();
GenerationContext gc = new GenerationContext(nm, null, cm, opts, io);
Class<?>[] argumentTypes = {Object.class, double.class, int.class, long.class};
NormalMethod callee = getNormalMethodForTest("emptyInstanceMethodWithParams", argumentTypes);
MethodOperand methOp = MethodOperand.VIRTUAL(callee.getMemberRef().asMethodReference(), callee);
Instruction callInstr = Call.create(CALL, null, null, methOp, 5);
RegisterOperand receiver = createMockRegisterOperand(TypeReference.JavaLangObject);
assertFalse(receiver.isPreciseType());
assertFalse(receiver.isDeclaredType());
receiver.setPreciseType();
Call.setParam(callInstr, 0, receiver);
RegisterOperand objectParam = prepareCallWithObjectParam(callInstr);
RegisterOperand doubleParam = prepareCallWithDoubleParam(callInstr);
RegisterOperand intParam = prepareCallWithIntParam(callInstr);
RegisterOperand longParam = prepareCallWithLongParam(callInstr);
callInstr.setPosition(new InlineSequence(nm));
ExceptionHandlerBasicBlockBag ebag = getMockEbag();
int nodeNumber = 12345;
gc.getCfg().setNumberOfNodes(nodeNumber);
GenerationContext child = gc.createChildContext(ebag, callee, callInstr);
assertThatStateIsCopiedFromParentToChild(gc, callee, child, ebag);
assertThatReturnValueIsVoid(child);
RegisterOperand thisArg = child.getArguments()[0].asRegister();
assertFalse(thisArg.isPreciseType());
assertTrue(thisArg.isDeclaredType());
TypeReference calleeClass = callee.getDeclaringClass().getTypeRef();
assertSame(thisArg.getType(), calleeClass);
RegisterOperand expectedLocalForReceiverParam = child.makeLocal(0, thisArg);
assertTrue(thisArg.sameRegisterPropertiesAs(expectedLocalForReceiverParam));
RegisterOperand firstArg = child.getArguments()[1].asRegister();
RegisterOperand expectedLocalForObjectParam = child.makeLocal(1, firstArg);
assertTrue(firstArg.sameRegisterPropertiesAs(expectedLocalForObjectParam));
RegisterOperand secondArg = child.getArguments()[2].asRegister();
RegisterOperand expectedLocalForDoubleParam = child.makeLocal(2, secondArg);
assertTrue(secondArg.sameRegisterPropertiesAs(expectedLocalForDoubleParam));
RegisterOperand thirdArg = child.getArguments()[3].asRegister();
RegisterOperand expectedLocalForIntParam = child.makeLocal(4, thirdArg);
assertTrue(thirdArg.sameRegisterPropertiesAs(expectedLocalForIntParam));
RegisterOperand fourthArg = child.getArguments()[4].asRegister();
RegisterOperand expectedLocalForLongParam = child.makeLocal(5, fourthArg);
assertTrue(fourthArg.sameRegisterPropertiesAs(expectedLocalForLongParam));
InlineSequence expectedInlineSequence = new InlineSequence(callee, callInstr.position(), callInstr);
assertEquals(expectedInlineSequence, child.getInlineSequence());
assertThatPrologueAndEpilogueAreWiredCorrectlyForChildContext(ebag, nodeNumber, child);
Enumeration<Instruction> prologueRealInstr = child.getPrologue().forwardRealInstrEnumerator();
Instruction receiverMove = prologueRealInstr.nextElement();
RegisterOperand expectedReceiver = receiver.copy().asRegister();
narrowRegOpToCalleeClass(expectedReceiver, calleeClass);
assertMoveOperationIsCorrect(callInstr, REF_MOVE, expectedLocalForReceiverParam, expectedReceiver, receiverMove);
Instruction objectMove = prologueRealInstr.nextElement();
RegisterOperand objectParamCopy = objectParam.copy().asRegister();
assertMoveOperationIsCorrect(callInstr, REF_MOVE, expectedLocalForObjectParam, objectParamCopy, objectMove);
Instruction doubleMove = prologueRealInstr.nextElement();
RegisterOperand doubleParamCopy = doubleParam.copy().asRegister();
assertMoveOperationIsCorrect(callInstr, DOUBLE_MOVE, expectedLocalForDoubleParam, doubleParamCopy, doubleMove);
Instruction intMove = prologueRealInstr.nextElement();
RegisterOperand intParamCopy = intParam.copy().asRegister();
assertMoveOperationIsCorrect(callInstr, INT_MOVE, expectedLocalForIntParam, intParamCopy, intMove);
Instruction longMove = prologueRealInstr.nextElement();
RegisterOperand longParamCopy = longParam.copy().asRegister();
assertMoveOperationIsCorrect(callInstr, LONG_MOVE, expectedLocalForLongParam, longParamCopy, longMove);
assertThatNoMoreInstructionsExist(prologueRealInstr);
BasicBlock epilogue = child.getEpilogue();
assertThatEpilogueLabelIsCorrectForInlinedMethod(child,
expectedInlineSequence, epilogue);
assertThatEpilogueIsEmpty(epilogue);
assertThatNoRethrowBlockExists(child);
assertThatChecksWontBeSkipped(gc);
}
private void assertThatEpilogueIsEmpty(BasicBlock epilogue) {
assertTrue(epilogue.isEmpty());
}
private void assertThatEpilogueLabelIsCorrectForInlinedMethod(
GenerationContext child, InlineSequence inlineSequence,
BasicBlock epilogue) {
assertThat(child.getCfg().lastInCodeOrder(), is(epilogue));
assertThat(epilogue.firstInstruction().getBytecodeIndex(), is(EPILOGUE_BCI));
assertTrue(epilogue.firstInstruction().position().equals(inlineSequence));
}
private void assertThatNoRethrowBlockExists(GenerationContext child) {
assertNull(child.getUnlockAndRethrow());
}
private void assertThatNoMoreInstructionsExist(
Enumeration<Instruction> instrEnumeration) {
assertFalse(instrEnumeration.hasMoreElements());
}
private void assertThatPrologueAndEpilogueAreWiredCorrectlyForChildContext(
ExceptionHandlerBasicBlockBag ebag, int nodeNumber,
GenerationContext child) {
assertThat(child.getPrologue().firstInstruction().getBytecodeIndex(), is(PROLOGUE_BCI));
assertThat(child.getPrologue().firstInstruction().position(), is(child.getInlineSequence()));
assertThat(child.getPrologue().getNumber(), is(nodeNumber));
assertThat(child.getPrologue().exceptionHandlers(), is(ebag));
assertThat(child.getEpilogue().firstInstruction().getBytecodeIndex(), is(EPILOGUE_BCI));
assertThat(child.getEpilogue().firstInstruction().position(), is(child.getInlineSequence()));
assertThat(child.getEpilogue().getNumber(), is(nodeNumber + 1));
assertThat(child.getEpilogue().exceptionHandlers(), is(ebag));
int newNodeNumber = nodeNumber + 2;
assertThat(child.getCfg().numberOfNodes(), is(newNodeNumber));
assertThat(child.getCfg().firstInCodeOrder(), is(child.getPrologue()));
assertThat(child.getCfg().lastInCodeOrder(), is(child.getEpilogue()));
}
@Test
public void inliningInstanceMethodWithRegisterReceiverNoNarrowing() throws Exception {
NormalMethod nm = getNormalMethodForTest("methodForInliningTests");
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = new OptOptions();
InlineOracle io = new DefaultInlineOracle();
GenerationContext gc = new GenerationContext(nm, null, cm, opts, io);
Class<?>[] argumentTypes = {Object.class, double.class, int.class, long.class};
NormalMethod callee = getNormalMethodForTest("emptyInstanceMethodWithParams", argumentTypes);
MethodOperand methOp = MethodOperand.VIRTUAL(callee.getMemberRef().asMethodReference(), callee);
Instruction callInstr = Call.create(CALL, null, null, methOp, 5);
RegisterOperand receiver = createMockRegisterOperand(callee.getDeclaringClass().getTypeRef());
assertFalse(receiver.isPreciseType());
assertFalse(receiver.isDeclaredType());
receiver.setPreciseType();
Call.setParam(callInstr, 0, receiver);
RegisterOperand objectParam = prepareCallWithObjectParam(callInstr);
RegisterOperand doubleParam = prepareCallWithDoubleParam(callInstr);
RegisterOperand intParam = prepareCallWithIntParam(callInstr);
RegisterOperand longParam = prepareCallWithLongParam(callInstr);
callInstr.setPosition(new InlineSequence(nm));
ExceptionHandlerBasicBlockBag ebag = getMockEbag();
int nodeNumber = 12345;
gc.getCfg().setNumberOfNodes(nodeNumber);
GenerationContext child = gc.createChildContext(ebag, callee, callInstr);
assertThatStateIsCopiedFromParentToChild(gc, callee, child, ebag);
assertThatReturnValueIsVoid(child);
RegisterOperand thisArg = child.getArguments()[0].asRegister();
TypeReference calleeClass = callee.getDeclaringClass().getTypeRef();
assertSame(thisArg.getType(), calleeClass);
RegisterOperand expectedLocalForReceiverParam = child.makeLocal(0, thisArg);
assertTrue(thisArg.sameRegisterPropertiesAs(expectedLocalForReceiverParam));
RegisterOperand firstArg = child.getArguments()[1].asRegister();
RegisterOperand expectedLocalForObjectParam = child.makeLocal(1, firstArg);
assertTrue(firstArg.sameRegisterPropertiesAs(expectedLocalForObjectParam));
RegisterOperand secondArg = child.getArguments()[2].asRegister();
RegisterOperand expectedLocalForDoubleParam = child.makeLocal(2, secondArg);
assertTrue(secondArg.sameRegisterPropertiesAs(expectedLocalForDoubleParam));
RegisterOperand thirdArg = child.getArguments()[3].asRegister();
RegisterOperand expectedLocalForIntParam = child.makeLocal(4, thirdArg);
assertTrue(thirdArg.sameRegisterPropertiesAs(expectedLocalForIntParam));
RegisterOperand fourthArg = child.getArguments()[4].asRegister();
RegisterOperand expectedLocalForLongParam = child.makeLocal(5, fourthArg);
assertTrue(fourthArg.sameRegisterPropertiesAs(expectedLocalForLongParam));
InlineSequence expectedInlineSequence = new InlineSequence(callee, callInstr.position(), callInstr);
assertEquals(expectedInlineSequence, child.getInlineSequence());
assertThatPrologueAndEpilogueAreWiredCorrectlyForChildContext(ebag, nodeNumber, child);
Enumeration<Instruction> prologueRealInstr = child.getPrologue().forwardRealInstrEnumerator();
Instruction receiverMove = prologueRealInstr.nextElement();
RegisterOperand expectedReceiver = receiver.copy().asRegister();
assertMoveOperationIsCorrect(callInstr, REF_MOVE, expectedLocalForReceiverParam, expectedReceiver, receiverMove);
Instruction objectMove = prologueRealInstr.nextElement();
RegisterOperand objectParamCopy = objectParam.copy().asRegister();
assertMoveOperationIsCorrect(callInstr, REF_MOVE, expectedLocalForObjectParam, objectParamCopy, objectMove);
Instruction doubleMove = prologueRealInstr.nextElement();
RegisterOperand doubleParamCopy = doubleParam.copy().asRegister();
assertMoveOperationIsCorrect(callInstr, DOUBLE_MOVE, expectedLocalForDoubleParam, doubleParamCopy, doubleMove);
Instruction intMove = prologueRealInstr.nextElement();
RegisterOperand intParamCopy = intParam.copy().asRegister();
assertMoveOperationIsCorrect(callInstr, INT_MOVE, expectedLocalForIntParam, intParamCopy, intMove);
Instruction longMove = prologueRealInstr.nextElement();
RegisterOperand longParamCopy = longParam.copy().asRegister();
assertMoveOperationIsCorrect(callInstr, LONG_MOVE, expectedLocalForLongParam, longParamCopy, longMove);
assertThatNoMoreInstructionsExist(prologueRealInstr);
BasicBlock epilogue = child.getEpilogue();
assertThatEpilogueLabelIsCorrectForInlinedMethod(child,
expectedInlineSequence, epilogue);
assertThatEpilogueIsEmpty(epilogue);
assertThatNoRethrowBlockExists(child);
assertThatChecksWontBeSkipped(gc);
}
private RegisterOperand prepareCallWithLongParam(Instruction callInstr) {
RegisterOperand longParam = createMockRegisterOperand(TypeReference.Long);
Call.setParam(callInstr, 4, longParam);
return longParam;
}
private RegisterOperand prepareCallWithIntParam(Instruction callInstr) {
RegisterOperand intParam = createMockRegisterOperand(TypeReference.Int);
Call.setParam(callInstr, 3, intParam);
return intParam;
}
private RegisterOperand prepareCallWithDoubleParam(Instruction callInstr) {
RegisterOperand doubleParam = createMockRegisterOperand(TypeReference.Double);
Call.setParam(callInstr, 2, doubleParam);
return doubleParam;
}
@Test
public void inliningMethodWithConstantReceiver() throws Exception {
NormalMethod nm = getNormalMethodForTest("methodForInliningTests");
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = new OptOptions();
InlineOracle io = new DefaultInlineOracle();
GenerationContext gc = new GenerationContext(nm, null, cm, opts, io);
Class<?>[] argumentTypes = {Object.class, double.class, int.class, long.class};
NormalMethod callee = getNormalMethodForTest("emptyInstanceMethodWithParams", argumentTypes);
MethodOperand methOp = MethodOperand.VIRTUAL(callee.getMemberRef().asMethodReference(), callee);
Instruction callInstr = Call.create(CALL, null, null, methOp, 5);
ObjectConstantOperand receiver = new ObjectConstantOperand(new MethodsForTests(), null);
Call.setParam(callInstr, 0, receiver);
RegisterOperand objectParam = prepareCallWithObjectParam(callInstr);
RegisterOperand doubleParam = prepareCallWithDoubleParam(callInstr);
RegisterOperand intParam = prepareCallWithIntParam(callInstr);
RegisterOperand longParam = prepareCallWithLongParam(callInstr);
callInstr.setPosition(new InlineSequence(nm));
ExceptionHandlerBasicBlockBag ebag = getMockEbag();
int nodeNumber = 12345;
gc.getCfg().setNumberOfNodes(nodeNumber);
GenerationContext child = gc.createChildContext(ebag, callee, callInstr);
assertThatStateIsCopiedFromParentToChild(gc, callee, child, ebag);
assertThatReturnValueIsVoid(child);
Operand thisArg = child.getArguments()[0];
RegisterOperand expectedLocalForReceiverParam = child.makeLocal(0, thisArg.getType());
expectedLocalForReceiverParam.setPreciseType();
RegisterOperand expectedNullCheckGuard = child.makeNullCheckGuard(expectedLocalForReceiverParam.getRegister());
BC2IR.setGuardForRegOp(expectedLocalForReceiverParam, expectedNullCheckGuard);
assertNotNull(expectedNullCheckGuard);
RegisterOperand firstArg = child.getArguments()[1].asRegister();
RegisterOperand expectedLocalForObjectParam = child.makeLocal(1, firstArg);
assertTrue(firstArg.sameRegisterPropertiesAs(expectedLocalForObjectParam));
RegisterOperand secondArg = child.getArguments()[2].asRegister();
RegisterOperand expectedLocalForDoubleParam = child.makeLocal(2, secondArg);
assertTrue(secondArg.sameRegisterPropertiesAs(expectedLocalForDoubleParam));
RegisterOperand thirdArg = child.getArguments()[3].asRegister();
RegisterOperand expectedLocalForIntParam = child.makeLocal(4, thirdArg);
assertTrue(thirdArg.sameRegisterPropertiesAs(expectedLocalForIntParam));
RegisterOperand fourthArg = child.getArguments()[4].asRegister();
RegisterOperand expectedLocalForLongParam = child.makeLocal(5, fourthArg);
assertTrue(fourthArg.sameRegisterPropertiesAs(expectedLocalForLongParam));
InlineSequence expectedInlineSequence = new InlineSequence(callee, callInstr.position(), callInstr);
assertEquals(expectedInlineSequence, child.getInlineSequence());
assertThatPrologueAndEpilogueAreWiredCorrectlyForChildContext(ebag, nodeNumber, child);
Enumeration<Instruction> prologueRealInstr = child.getPrologue().forwardRealInstrEnumerator();
Instruction guardMove = prologueRealInstr.nextElement();
assertThat(guardMove.operator(), is(GUARD_MOVE));
assertThat(guardMove.getBytecodeIndex(), is(UNKNOWN_BCI));
RegisterOperand guardMoveResult = Move.getResult(guardMove);
assertTrue(guardMoveResult.sameRegisterPropertiesAs(expectedNullCheckGuard));
Operand moveValue = Move.getVal(guardMove);
assertTrue(moveValue.isTrueGuard());
assertNull(guardMove.position());
Instruction receiverMove = prologueRealInstr.nextElement();
Operand expectedReceiver = receiver.copy();
//TODO definite non-nullness of constant operand is not being verified
assertSame(callInstr.position(), receiverMove.position());
assertThat(receiverMove.operator(), is(REF_MOVE));
assertThat(receiverMove.getBytecodeIndex(), is(PROLOGUE_BCI));
RegisterOperand receiverMoveResult = Move.getResult(receiverMove);
assertTrue(receiverMoveResult.sameRegisterPropertiesAsExceptForGuardWhichIsSimilar(expectedLocalForReceiverParam));
Operand receiverMoveValue = Move.getVal(receiverMove);
assertTrue(receiverMoveValue.similar(expectedReceiver));
Instruction objectMove = prologueRealInstr.nextElement();
RegisterOperand objectParamCopy = objectParam.copy().asRegister();
assertMoveOperationIsCorrect(callInstr, REF_MOVE, expectedLocalForObjectParam, objectParamCopy, objectMove);
Instruction doubleMove = prologueRealInstr.nextElement();
RegisterOperand doubleParamCopy = doubleParam.copy().asRegister();
assertMoveOperationIsCorrect(callInstr, DOUBLE_MOVE, expectedLocalForDoubleParam, doubleParamCopy, doubleMove);
Instruction intMove = prologueRealInstr.nextElement();
RegisterOperand intParamCopy = intParam.copy().asRegister();
assertMoveOperationIsCorrect(callInstr, INT_MOVE, expectedLocalForIntParam, intParamCopy, intMove);
Instruction longMove = prologueRealInstr.nextElement();
RegisterOperand longParamCopy = longParam.copy().asRegister();
assertMoveOperationIsCorrect(callInstr, LONG_MOVE, expectedLocalForLongParam, longParamCopy, longMove);
assertThatNoMoreInstructionsExist(prologueRealInstr);
BasicBlock epilogue = child.getEpilogue();
assertThatEpilogueLabelIsCorrectForInlinedMethod(child,
expectedInlineSequence, epilogue);
assertThatEpilogueIsEmpty(epilogue);
assertThatNoRethrowBlockExists(child);
assertThatChecksWontBeSkipped(gc);
}
private RegisterOperand prepareCallWithObjectParam(Instruction callInstr) {
RegisterOperand objectParam = createMockRegisterOperand(TypeReference.JavaLangObject);
Call.setParam(callInstr, 1, objectParam);
return objectParam;
}
@Test(expected = OptimizingCompilerException.class)
public void invalidReceiverCausesException() throws Exception {
NormalMethod nm = getNormalMethodForTest("methodForInliningTests");
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = new OptOptions();
InlineOracle io = new DefaultInlineOracle();
GenerationContext gc = new GenerationContext(nm, null, cm, opts, io);
NormalMethod callee = getNormalMethodForTest("emptyInstanceMethodWithoutAnnotations");
MethodOperand methOp = MethodOperand.VIRTUAL(callee.getMemberRef().asMethodReference(), callee);
Instruction callInstr = Call.create(CALL, null, null, methOp, 1);
Operand receiver = new InvalidReceiverOperand();
Call.setParam(callInstr, 0, receiver);
callInstr.setPosition(new InlineSequence(nm));
ExceptionHandlerBasicBlockBag ebag = getMockEbag();
gc.createChildContext(ebag, callee, callInstr);
}
private static class InvalidReceiverOperand extends Operand {
@Override
public Operand copy() {
return new InvalidReceiverOperand();
}
@Override
public boolean similar(Operand op) {
return op instanceof InvalidReceiverOperand;
}
}
@Test
public void inliningInstanceMethodWithNarrowingOfReferenceParam() throws Exception {
NormalMethod nm = getNormalMethodForTest("methodForInliningTests");
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = new OptOptions();
InlineOracle io = new DefaultInlineOracle();
GenerationContext gc = new GenerationContext(nm, null, cm, opts, io);
Class<?>[] argumentTypes = {MethodsForTests.class};
NormalMethod callee = getNormalMethodForTest("emptyStaticMethodWithReferenceParam", argumentTypes);
MethodOperand methOp = MethodOperand.VIRTUAL(callee.getMemberRef().asMethodReference(), callee);
Instruction callInstr = Call.create(CALL, null, null, methOp, 1);
RegisterOperand objectParam = createMockRegisterOperand(TypeReference.JavaLangObject);
assertFalse(objectParam.isPreciseType());
assertFalse(objectParam.isDeclaredType());
objectParam.setPreciseType();
Call.setParam(callInstr, 0, objectParam);
RegisterOperand objectParamCopy = objectParam.copy().asRegister();
callInstr.setPosition(new InlineSequence(nm));
ExceptionHandlerBasicBlockBag ebag = getMockEbag();
int nodeNumber = 12345;
gc.getCfg().setNumberOfNodes(nodeNumber);
GenerationContext child = gc.createChildContext(ebag, callee, callInstr);
assertThatStateIsCopiedFromParentToChild(gc, callee, child, ebag);
assertThatReturnValueIsVoid(child);
RegisterOperand objectParamArg = child.getArguments()[0].asRegister();
TypeReference calleeClass = callee.getDeclaringClass().getTypeRef();
assertThatRegOpWasNarrowedToCalleeClass(objectParamArg, calleeClass);
RegisterOperand expectedLocalForObjectParam = child.makeLocal(0, objectParamArg);
assertTrue(objectParamArg.sameRegisterPropertiesAs(expectedLocalForObjectParam));
InlineSequence expectedInlineSequence = new InlineSequence(callee, callInstr.position(), callInstr);
assertEquals(expectedInlineSequence, child.getInlineSequence());
assertThatPrologueAndEpilogueAreWiredCorrectlyForChildContext(ebag, nodeNumber, child);
Enumeration<Instruction> prologueRealInstr = child.getPrologue().forwardRealInstrEnumerator();
Instruction objectMove = prologueRealInstr.nextElement();
narrowRegOpToCalleeClass(objectParamCopy, calleeClass);
assertMoveOperationIsCorrect(callInstr, REF_MOVE, expectedLocalForObjectParam, objectParamCopy, objectMove);
assertThatNoMoreInstructionsExist(prologueRealInstr);
BasicBlock epilogue = child.getEpilogue();
assertThatEpilogueLabelIsCorrectForInlinedMethod(child,
expectedInlineSequence, epilogue);
assertThatEpilogueIsEmpty(epilogue);
assertThatNoRethrowBlockExists(child);
assertThatChecksWontBeSkipped(gc);
}
private void narrowRegOpToCalleeClass(RegisterOperand expectedObjectParam,
TypeReference calleeClass) {
expectedObjectParam.clearPreciseType();
expectedObjectParam.setType(calleeClass);
expectedObjectParam.setDeclaredType();
}
private void assertThatRegOpWasNarrowedToCalleeClass(RegisterOperand regOp,
TypeReference calleeClass) {
assertFalse(regOp.isPreciseType());
assertTrue(regOp.isDeclaredType());
assertSame(regOp.getType(), calleeClass);
}
@Test
public void annotationsAreTreatedCorrectlyForInlinedMethods() throws Exception {
NormalMethod nm = getNormalMethodForTest("methodForInliningTests");
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = new OptOptions();
InlineOracle io = new DefaultInlineOracle();
GenerationContext gc = new GenerationContext(nm, null, cm, opts, io);
ExceptionHandlerBasicBlockBag ebag = getMockEbag();
NormalMethod callee = getNormalMethodForTest("emptyStaticMethodWithNoBoundCheckAnnotation");
Instruction noBoundsInstr = buildCallInstructionForStaticMethodWithoutReturn(callee, nm);
GenerationContext noBoundsContext = gc.createChildContext(ebag, callee, noBoundsInstr);
assertTrue(noBoundsContext.noBoundsChecks());
assertFalse(noBoundsContext.noNullChecks());
assertFalse(noBoundsContext.noCheckStoreChecks());
callee = getNormalMethodForTest("emptyStaticMethodWithNoCheckStoreAnnotation");
Instruction noCheckStoreInstr = buildCallInstructionForStaticMethodWithoutReturn(callee, nm);
GenerationContext noCheckStoreContext = gc.createChildContext(ebag, callee, noCheckStoreInstr);
assertFalse(noCheckStoreContext.noBoundsChecks());
assertFalse(noCheckStoreContext.noNullChecks());
assertTrue(noCheckStoreContext.noCheckStoreChecks());
callee = getNormalMethodForTest("emptyStaticMethodWithNoNullCheckAnnotation");
Instruction noNullChecks = buildCallInstructionForStaticMethodWithoutReturn(callee, nm);
GenerationContext noNullCheckContext = gc.createChildContext(ebag, callee, noNullChecks);
assertFalse(noNullCheckContext.noBoundsChecks());
assertTrue(noNullCheckContext.noNullChecks());
assertFalse(noNullCheckContext.noCheckStoreChecks());
}
private Instruction buildCallInstructionForStaticMethodWithoutReturn(
NormalMethod callee, NormalMethod caller) {
MethodOperand methOp = MethodOperand.STATIC(callee);
Instruction callInstr = Call.create(CALL, null, null, methOp, 0);
callInstr.setPosition(new InlineSequence(caller));
return callInstr;
}
@Test
public void rethrowBlockIsCorrectForSynchronizedMethodInlinedIntoSynchronizedMethod() throws Exception {
NormalMethod nm = getNormalMethodForTest("emptySynchronizedStaticMethod");
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = new OptOptions();
InlineOracle io = new DefaultInlineOracle();
GenerationContext gc = new GenerationContext(nm, null, cm, opts, io);
NormalMethod callee = getNormalMethodForTest("emptySynchronizedStaticMethod");
Instruction callInstr = buildCallInstructionForStaticMethodWithoutReturn(callee, nm);
ExceptionHandlerBasicBlockBag ebag = gc.getEnclosingHandlers();
GenerationContext childContext = gc.createChildContext(ebag, callee, callInstr);
assertThatExceptionHandlersWereGenerated(childContext);
Operand expectedLockObject = buildLockObjectForStaticMethod(callee);
assertThatUnlockAndRethrowBlockIsCorrectForInlinedMethod(gc, childContext, childContext.getInlineSequence(),
childContext.getPrologue(), expectedLockObject, childContext.getEpilogue());
assertThatChecksWontBeSkipped(gc);
}
private void assertThatUnlockAndRethrowBlockIsCorrectForInlinedMethod(GenerationContext parentContext,
GenerationContext childContext, InlineSequence inlineSequence, BasicBlock prologue,
Operand lockObject, BasicBlock epilogue) {
ExceptionHandlerBasicBlockBag ehbb = childContext.getEnclosingHandlers();
Enumeration<BasicBlock> enumerator = ehbb.enumerator();
assertThat(childContext.getUnlockAndRethrow().exceptionHandlers(), is(parentContext.getEnclosingHandlers()));
ExceptionHandlerBasicBlock rethrow = (ExceptionHandlerBasicBlock) enumerator.nextElement();
assertSame(rethrow, childContext.getUnlockAndRethrow());
assertThatRethrowBlockIsCorrect(inlineSequence, lockObject, rethrow);
checkCodeOrderForRethrowBlock(childContext, prologue, epilogue, rethrow);
ExceptionHandlerBasicBlockBag parentHandlers = parentContext.getEnclosingHandlers();
Enumeration<BasicBlock> parentHandlerBBEnum = parentHandlers.enumerator();
HashSet<BasicBlock> parentHandlerBBs = new HashSet<BasicBlock>();
while (parentHandlerBBEnum.hasMoreElements()) {
parentHandlerBBs.add(parentHandlerBBEnum.nextElement());
}
BasicBlock childRethrow = childContext.getUnlockAndRethrow();
OutEdgeEnumeration outEdges = childRethrow.outEdges();
boolean linkedToAllBlocksFromParentHandler = true;
while (outEdges.hasMoreElements()) {
BasicBlock target = (BasicBlock) outEdges.nextElement().to();
if (!parentHandlerBBs.contains(target) && target != childContext.getExit()) {
linkedToAllBlocksFromParentHandler = false;
break;
}
}
assertTrue(linkedToAllBlocksFromParentHandler);
ExceptionHandlerBasicBlockBag ehbbb = childContext.getEnclosingHandlers();
assertSame(parentContext.getEnclosingHandlers(), ehbbb.getCaller());
assertThatEnclosingHandlersContainRethrow(rethrow, ehbbb);
}
private void assertThatEnclosingHandlersContainRethrow(
ExceptionHandlerBasicBlock rethrow, ExceptionHandlerBasicBlockBag ehbbb) {
boolean rethrowFound = false;
Enumeration<BasicBlock> exceptionHandlerBasicBlocks = ehbbb.enumerator();
while (exceptionHandlerBasicBlocks.hasMoreElements()) {
BasicBlock nextElement = exceptionHandlerBasicBlocks.nextElement();
if (nextElement == rethrow) {
rethrowFound = true;
}
}
assertTrue(rethrowFound);
}
private void assertThatRethrowBlockIsCorrect(InlineSequence inlineSequence,
Operand lockObject, ExceptionHandlerBasicBlock rethrow) {
Enumeration<TypeOperand> exceptionTypes = rethrow.getExceptionTypes();
TypeOperand firstHandledException = exceptionTypes.nextElement();
assertThat(exceptionTypes.hasMoreElements(), is(false));
assertTrue(firstHandledException.similar(new TypeOperand(RVMType.JavaLangThrowableType)));
Enumeration<Instruction> rethrowInstructions = rethrow.forwardRealInstrEnumerator();
Instruction firstInstructionInRethrow = rethrowInstructions.nextElement();
assertThat(firstInstructionInRethrow.operator(), is(GET_CAUGHT_EXCEPTION));
assertThat(firstInstructionInRethrow.getBytecodeIndex(), is(SYNTH_CATCH_BCI));
assertTrue(firstInstructionInRethrow.position().equals(inlineSequence));
RegisterOperand catchExceptionObject = Nullary.getResult(firstInstructionInRethrow);
assertThat(catchExceptionObject.getType(), is(TypeReference.JavaLangThrowable));
Instruction secondInstructionInRethrow = rethrowInstructions.nextElement();
assertThatNoMoreInstructionsExist(rethrowInstructions);
assertThat(secondInstructionInRethrow.operator(), is(CALL));
MethodOperand methodOp = Call.getMethod(secondInstructionInRethrow);
assertTrue(methodOp.getTarget().equals(Entrypoints.unlockAndThrowMethod));
assertTrue(methodOp.isNonReturningCall());
Operand callAddress = Call.getAddress(secondInstructionInRethrow);
Address actualAddress = callAddress.asAddressConstant().value;
Address expectedAddress = Entrypoints.unlockAndThrowMethod.getOffset().toWord().toAddress();
assertTrue(actualAddress.EQ(expectedAddress));
Operand lockObjectFromRethrow = Call.getParam(secondInstructionInRethrow, 0);
assertTrue(lockObjectFromRethrow.similar(lockObject));
RegisterOperand catchOperand = Call.getParam(secondInstructionInRethrow, 1).asRegister();
assertTrue(catchOperand.sameRegisterPropertiesAs(catchExceptionObject));
assertTrue(rethrow.mayThrowUncaughtException());
assertTrue(rethrow.canThrowExceptions());
}
@Test
public void unintBeginAndUnintEndAreAddedWhenNecessary() throws Exception {
NormalMethod nm = getNormalMethodForTest("emptyStaticMethodWithoutAnnotations");
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = new OptOptions();
InlineOracle io = new DefaultInlineOracle();
GenerationContext gc = new GenerationContext(nm, null, cm, opts, io);
assertThatStateIsCorrectForUnsynchronizedEmptyStaticMethod(nm, cm, io, gc);
NormalMethod callee = getNormalMethodForTest("emptyStaticUninterruptibleMethod");
Instruction callInstr = buildCallInstructionForStaticMethodWithoutReturn(callee, nm);
ExceptionHandlerBasicBlockBag ebag = getMockEbag();
int nodeNumber = 23456;
gc.getCfg().setNumberOfNodes(nodeNumber);
GenerationContext child = gc.createChildContext(ebag, callee, callInstr);
assertThatStateIsCopiedFromParentToChild(gc, callee, child, ebag);
InlineSequence expectedInlineSequence = new InlineSequence(callee, callInstr.position(), callInstr);
assertEquals(expectedInlineSequence, child.getInlineSequence());
assertThatPrologueAndEpilogueAreWiredCorrectlyForChildContext(ebag, nodeNumber, child);
Enumeration<Instruction> prologueRealInstr = child.getPrologue().forwardRealInstrEnumerator();
Instruction unintBegin = prologueRealInstr.nextElement();
assertThatInstructionIsUnintMarker(unintBegin, UNINT_BEGIN);
assertThatNoMoreInstructionsExist(prologueRealInstr);
Enumeration<Instruction> epilogueRealInstr = child.getEpilogue().forwardRealInstrEnumerator();
Instruction unintEnd = epilogueRealInstr.nextElement();
assertThatInstructionIsUnintMarker(unintEnd, UNINT_END);
assertThatNoMoreInstructionsExist(epilogueRealInstr);
}
@Test
public void unintBeginAndUnintEndAreNotAddedWhenDirectParentHasThem() throws Exception {
NormalMethod nm = getNormalMethodForTest("emptyStaticUninterruptibleMethod");
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = new OptOptions();
InlineOracle io = new DefaultInlineOracle();
GenerationContext gc = new GenerationContext(nm, null, cm, opts, io);
assertThatStateIsCorrectForUnsynchronizedEmptyStaticMethod(nm, cm, io, gc);
NormalMethod interruptibleCallee = getNormalMethodForTest("emptyStaticUninterruptibleMethod");
Instruction callInstr = buildCallInstructionForStaticMethodWithoutReturn(interruptibleCallee, nm);
ExceptionHandlerBasicBlockBag ebag = getMockEbag();
GenerationContext child = gc.createChildContext(ebag, interruptibleCallee, callInstr);
Enumeration<Instruction> prologueRealInstr = child.getPrologue().forwardRealInstrEnumerator();
assertThatNoMoreInstructionsExist(prologueRealInstr);
Enumeration<Instruction> epilogueRealInstr = child.getEpilogue().forwardRealInstrEnumerator();
assertThatNoMoreInstructionsExist(epilogueRealInstr);
}
private ExceptionHandlerBasicBlockBag getMockEbag() {
ExceptionHandlerBasicBlockBag ebag = new ExceptionHandlerBasicBlockBag(null, null);
return ebag;
}
private void assertThatInstructionIsUnintMarker(Instruction instr,
Operator markerType) {
assertTrue(Empty.conforms(instr));
assertThat(instr.operator(), is(markerType));
}
@Test
public void transferStateUpdatesFieldsInParentFromChild() throws Exception {
NormalMethod nm = getNormalMethodForTest("emptyStaticMethodWithoutAnnotations");
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = new OptOptions();
InlineOracle io = new DefaultInlineOracle();
GenerationContext parent = new GenerationContext(nm, null, cm, opts, io);
int targetNumberOfNodes = 23456789;
assertThatContextIsInExpectedState(parent, targetNumberOfNodes);
NormalMethod interruptibleCallee = getNormalMethodForTest("emptyStaticMethodWithoutAnnotations");
Instruction callInstr = buildCallInstructionForStaticMethodWithoutReturn(interruptibleCallee, nm);
ExceptionHandlerBasicBlockBag ebag = getMockEbag();
GenerationContext child = parent.createChildContext(ebag, interruptibleCallee, callInstr);
setTransferableProperties(targetNumberOfNodes, child);
child.transferStateToParent();
assertThatStateWasTransferedToOtherContext(parent, targetNumberOfNodes);
}
private void setTransferableProperties(int targetNumberOfNodes,
GenerationContext context) {
context.forceFrameAllocation();
context.markExceptionHandlersAsGenerated();
context.getCfg().setNumberOfNodes(targetNumberOfNodes);
}
private void assertThatStateWasTransferedToOtherContext(
GenerationContext otherContext, int targetNumberOfNodes) {
assertTrue(otherContext.requiresStackFrame());
assertTrue(otherContext.generatedExceptionHandlers());
assertTrue(otherContext.getCfg().numberOfNodes() == targetNumberOfNodes);
}
private void assertThatContextIsInExpectedState(GenerationContext parent,
int targetNumberOfNodes) {
assertFalse(parent.requiresStackFrame());
assertFalse(parent.generatedExceptionHandlers());
assertFalse("Assumption in test case wrong, need to change test case", parent.getCfg().numberOfNodes() == targetNumberOfNodes);
}
@Test(expected = IllegalStateException.class)
public void transferStateThrowsExceptionForContextsWithoutParents() throws Exception {
GenerationContext gc = createMostlyEmptyContext("emptyStaticMethodWithNoBoundCheckAnnotation");
gc.transferStateToParent();
}
@Test(expected = NullPointerException.class)
public void contextReturnedByGetSynthethicContextContainsOnlyCFG() throws Exception {
GenerationContext gc = createMostlyEmptyContext("methodForInliningTests");
ExceptionHandlerBasicBlockBag mockEbag = getMockEbag();
int parentCfgNodeNumber = gc.getCfg().numberOfNodes();
GenerationContext synthethicContext = GenerationContext.createSynthetic(gc, mockEbag);
int synthethicNodeNumber = -100000;
assertThat(synthethicContext.getCfg().numberOfNodes(), is(synthethicNodeNumber));
assertThat(synthethicContext.getPrologue().firstInstruction().getBytecodeIndex(), is(PROLOGUE_BCI));
assertThat(synthethicContext.getPrologue().firstInstruction().position(), is(gc.getInlineSequence()));
assertThat(synthethicContext.getPrologue().getNumber(), is(parentCfgNodeNumber));
assertThat(synthethicContext.getPrologue().exceptionHandlers(), is(mockEbag));
assertThat(synthethicContext.getEpilogue().firstInstruction().getBytecodeIndex(), is(EPILOGUE_BCI));
assertThat(synthethicContext.getEpilogue().firstInstruction().position(), is(gc.getInlineSequence()));
assertThat(synthethicContext.getEpilogue().getNumber(), is(parentCfgNodeNumber + 1));
assertThat(synthethicContext.getEpilogue().exceptionHandlers(), is(mockEbag));
assertThat(gc.getCfg().numberOfNodes(), is(4));
assertThat(synthethicContext.getCfg().numberOfNodes(), is(synthethicNodeNumber));
assertThat(synthethicContext.getCfg().firstInCodeOrder(), is(synthethicContext.getPrologue()));
assertThat(synthethicContext.getCfg().lastInCodeOrder(), is(synthethicContext.getEpilogue()));
assertFalse(synthethicContext.requiresStackFrame());
assertNull(synthethicContext.getArguments());
assertNull(synthethicContext.getBranchProfiles());
assertNull(synthethicContext.getEnclosingHandlers());
assertNull(synthethicContext.getExit());
assertFalse(synthethicContext.generatedExceptionHandlers());
assertNull(synthethicContext.getInlinePlan());
assertNull(synthethicContext.getInlineSequence());
assertNull(synthethicContext.getMethod());
assertNull(synthethicContext.getOptions());
assertNull(synthethicContext.getOriginalCompiledMethod());
assertNull(synthethicContext.getOriginalMethod());
assertNull(synthethicContext.getResult());
assertNull(synthethicContext.getResultReg());
assertNull(synthethicContext.getTemps());
assertNull(synthethicContext.getUnlockAndRethrow());
synthethicContext.resync(); // check that nc_guards are null
}
@Test
public void localRegReturnsRegistersThatAreFlaggedAsLocals() throws Exception {
NormalMethod nm = TestingTools.getNormalMethod(MethodsForTests.class, "emptyInstanceMethodWithoutAnnotations");
OptOptions opts = new OptOptions();
GenerationContext gc = new GenerationContext(nm, null, null, opts, null);
Register localReg = gc.localReg(0, nm.getDeclaringClass().getTypeRef());
assertNotNull(localReg);
assertTrue(localReg.isLocal());
}
@Test
public void localRegAlwaysReturnsSameRegisterForSameArguments() throws Exception {
NormalMethod nm = TestingTools.getNormalMethod(MethodsForTests.class, "emptyInstanceMethodWithoutAnnotations");
OptOptions opts = new OptOptions();
GenerationContext gc = new GenerationContext(nm, null, null, opts, null);
Register regExpected = gc.localReg(0, nm.getDeclaringClass().getTypeRef());
Register regActual = gc.localReg(0, nm.getDeclaringClass().getTypeRef());
assertSame(regActual, regExpected);
}
@Test(expected = ArrayIndexOutOfBoundsException.class)
public void localRegInvalidLocalNumberCausesArrayOutOfBoundsException() throws Exception {
GenerationContext gc = createMostlyEmptyContext("emptyInstanceMethodWithoutAnnotations");
Register reg = gc.localReg(Integer.MAX_VALUE, TypeReference.Int);
assertNotNull(reg);
}
@Test
public void localRegDoesNotCheckTypeOfLocals() throws Exception {
GenerationContext gc = createMostlyEmptyContext("emptyInstanceMethodWithoutAnnotations");
Register reg = gc.localReg(0, TypeReference.Void);
assertNotNull(reg);
}
@Test
public void localRegAllowsDifferentTypesForSameLocalNumber() throws Exception {
GenerationContext gc = createMostlyEmptyContext("emptyInstanceMethodWithoutAnnotations");
Register longReg = gc.localReg(0, TypeReference.Long);
assertNotNull(longReg);
Register referenceReg = gc.localReg(0, TypeReference.JavaLangObject);
assertNotNull(referenceReg);
Register intReg = gc.localReg(0, TypeReference.Int);
assertNotNull(intReg);
Register doubleReg = gc.localReg(0, TypeReference.Double);
assertNotNull(doubleReg);
Register floatReg = gc.localReg(0, TypeReference.Float);
assertNotNull(floatReg);
if (VM.BuildForOptCompiler) { // Type not available when opt compiler not included
Register validationReg = gc.localReg(0, TypeReference.VALIDATION_TYPE);
assertNotNull(validationReg);
}
}
@Test
public void localRegistersAreSavedInDifferentPools() throws Exception {
GenerationContext gc = createMostlyEmptyContext("emptyInstanceMethodWithoutAnnotations");
Register longReg = gc.localReg(0, TypeReference.Long);
Register referenceReg = gc.localReg(0, TypeReference.JavaLangObject);
assertNotSame(longReg, referenceReg);
Register intReg = gc.localReg(0, TypeReference.Int);
assertNotSame(longReg, intReg);
assertNotSame(referenceReg, intReg);
Register doubleReg = gc.localReg(0, TypeReference.Double);
assertNotSame(longReg, doubleReg);
assertNotSame(referenceReg, doubleReg);
assertNotSame(intReg, doubleReg);
Register floatReg = gc.localReg(0, TypeReference.Float);
assertNotSame(longReg, floatReg);
assertNotSame(referenceReg, floatReg);
assertNotSame(intReg, floatReg);
assertNotSame(doubleReg, floatReg);
}
@Test
public void makeLocalUsesLocalRegToDetermineRegisters() throws Exception {
NormalMethod nm = TestingTools.getNormalMethod(MethodsForTests.class, "emptyInstanceMethodWithoutAnnotations");
OptOptions opts = new OptOptions();
GenerationContext gc = new GenerationContext(nm, null, null, opts, null);
int localNumber = 0;
TypeReference localType = nm.getDeclaringClass().getTypeRef();
Register expectedRegister = gc.localReg(localNumber, localType);
RegisterOperand regOp = gc.makeLocal(localNumber, localType);
assertSame(expectedRegister, regOp.getRegister());
}
@Test
public void packagePrivateMakeLocalReturnsRegWithInheritedFlagsAndGuard() throws Exception {
NormalMethod nm = TestingTools.getNormalMethod(MethodsForTests.class, "emptyInstanceMethodWithoutAnnotations");
OptOptions opts = new OptOptions();
GenerationContext gc = new GenerationContext(nm, null, null, opts, null);
int localNumber = 0;
TypeReference localType = nm.getDeclaringClass().getTypeRef();
RegisterOperand regOp = gc.makeLocal(localNumber, localType);
TrueGuardOperand guard = new TrueGuardOperand();
regOp.setGuard(guard);
regOp.setParameter();
regOp.setNonVolatile();
regOp.setExtant();
regOp.setDeclaredType();
regOp.setPreciseType();
regOp.setPositiveInt();
RegisterOperand newRegOpWithInheritance = gc.makeLocal(localNumber, regOp);
Operand scratchObject = newRegOpWithInheritance.getGuard();
assertTrue(scratchObject.isTrueGuard());
assertTrue(newRegOpWithInheritance.isParameter());
assertTrue(newRegOpWithInheritance.isNonVolatile());
assertTrue(newRegOpWithInheritance.isExtant());
assertTrue(newRegOpWithInheritance.isDeclaredType());
assertTrue(newRegOpWithInheritance.isPreciseType());
assertTrue(newRegOpWithInheritance.isPositiveInt());
}
@Test
public void getLocalNumberForRegisterReturnsMinusOneIfNotALocal() throws Exception {
GenerationContext gc = createMostlyEmptyContext("emptyInstanceMethodWithoutAnnotations");
Register reg = new Register(currentRegisterNumber--);
reg.setLong();
reg.setLocal();
int localNumber = gc.getLocalNumberFor(reg, TypeReference.Long);
assertThat(localNumber, is(-1));
}
@Test
public void getLocalNumberReturnsLocalNumberForRegistersCreateViaMakeLocal() throws Exception {
Class<?>[] argumentTypes = {Object.class, double.class, int.class, long.class};
NormalMethod nm = getNormalMethodForTest("emptyInstanceMethodWithParams", argumentTypes);
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = new OptOptions();
InlineOracle io = new DefaultInlineOracle();
GenerationContext gc = new GenerationContext(nm, null, cm, opts, io);
int thisLocalNumber = 0;
TypeReference localType = nm.getDeclaringClass().getTypeRef();
RegisterOperand thisRegOp = gc.makeLocal(thisLocalNumber, localType);
Register thisReg = thisRegOp.getRegister();
assertThat(gc.getLocalNumberFor(thisReg, localType), is(thisLocalNumber));
}
@Test
public void isLocalReturnsTrueForRegistersCreatedViaMakeLocal() throws Exception {
Class<?>[] argumentTypes = {Object.class, double.class, int.class, long.class};
NormalMethod nm = getNormalMethodForTest("emptyInstanceMethodWithParams", argumentTypes);
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = new OptOptions();
InlineOracle io = new DefaultInlineOracle();
GenerationContext gc = new GenerationContext(nm, null, cm, opts, io);
int thisLocalNumber = 0;
TypeReference localType = nm.getDeclaringClass().getTypeRef();
RegisterOperand thisRegOp = gc.makeLocal(thisLocalNumber, localType);
assertTrue(gc.isLocal(thisRegOp, thisLocalNumber, localType));
}
@Test
public void makeNullCheckGuardReturnsSameGuardForSameRegister() throws Exception {
NormalMethod nm = TestingTools.getNormalMethod(MethodsForTests.class, "emptyInstanceMethodWithoutAnnotations");
OptOptions opts = new OptOptions();
GenerationContext gc = new GenerationContext(nm, null, null, opts, null);
int localNumber = 0;
TypeReference localType = nm.getDeclaringClass().getTypeRef();
RegisterOperand regOp = gc.makeLocal(localNumber, localType);
Register reg = regOp.getRegister();
RegisterOperand expectedNullCheckGuard = gc.makeNullCheckGuard(reg);
RegisterOperand actual = gc.makeNullCheckGuard(reg);
assertTrue(actual.sameRegisterPropertiesAs(expectedNullCheckGuard));
}
@Test
public void makeNullCheckGuardAlwaysReturnsCopies() throws Exception {
NormalMethod nm = TestingTools.getNormalMethod(MethodsForTests.class, "emptyInstanceMethodWithoutAnnotations");
OptOptions opts = new OptOptions();
GenerationContext gc = new GenerationContext(nm, null, null, opts, null);
int localNumber = 0;
TypeReference localType = nm.getDeclaringClass().getTypeRef();
RegisterOperand regOp = gc.makeLocal(localNumber, localType);
Register reg = regOp.getRegister();
RegisterOperand expectedNullCheckGuard = gc.makeNullCheckGuard(reg);
RegisterOperand copiedGuard = expectedNullCheckGuard.copy().asRegister();
expectedNullCheckGuard.setGuard(new TrueGuardOperand());
RegisterOperand actual = gc.makeNullCheckGuard(reg);
assertTrue(actual.sameRegisterPropertiesAs(copiedGuard));
assertNull(actual.getGuard());
}
@Test
public void resyncDeletesNullCheckGuardsThatMapToUnusedRegisters() throws Exception {
NormalMethod nm = TestingTools.getNormalMethod(MethodsForTests.class, "emptyInstanceMethodWithoutAnnotations");
OptOptions opts = new OptOptions();
GenerationContext gc = new GenerationContext(nm, null, null, opts, null);
RegisterOperand thisLocal = gc.makeLocal(0, nm.getDeclaringClass().getTypeRef());
Register thisReg = thisLocal.getRegister();
RegisterOperand thisNullCheckGuard = gc.makeNullCheckGuard(thisReg);
assertNotNull(thisNullCheckGuard);
gc.getTemps().removeRegister(thisReg);
gc.resync();
RegisterOperand newNullCheckGuard = gc.makeNullCheckGuard(thisReg);
assertFalse(newNullCheckGuard.sameRegisterPropertiesAs(thisNullCheckGuard));
}
@Test
public void resyncDoesNotDeleteNullCheckGuardsThatMapToUsedRegisters() throws Exception {
NormalMethod nm = TestingTools.getNormalMethod(MethodsForTests.class, "emptyInstanceMethodWithoutAnnotations");
OptOptions opts = new OptOptions();
GenerationContext gc = new GenerationContext(nm, null, null, opts, null);
RegisterOperand thisLocal = gc.makeLocal(0, nm.getDeclaringClass().getTypeRef());
Register thisReg = thisLocal.getRegister();
RegisterOperand thisNullCheckGuard = gc.makeNullCheckGuard(thisReg);
assertNotNull(thisNullCheckGuard);
gc.resync();
RegisterOperand newNullCheckGuard = gc.makeNullCheckGuard(thisReg);
assertTrue(newNullCheckGuard.sameRegisterPropertiesAs(thisNullCheckGuard));
}
@Test(expected = NullPointerException.class)
public void noNullCheckGuardsCanBeCreatedAfterCloseWasCalled() throws Exception {
NormalMethod nm = TestingTools.getNormalMethod(MethodsForTests.class, "emptyInstanceMethodWithoutAnnotations");
OptOptions opts = new OptOptions();
GenerationContext gc = new GenerationContext(nm, null, null, opts, null);
RegisterOperand thisLocal = gc.makeLocal(0, nm.getDeclaringClass().getTypeRef());
Register thisReg = thisLocal.getRegister();
gc.close();
gc.makeNullCheckGuard(thisReg);
}
@Test(expected = NullPointerException.class)
public void resyncMustNotBeCalledAfterClose() throws Exception {
GenerationContext gc = createMostlyEmptyContext("emptyStaticMethodWithoutAnnotations");
gc.close();
gc.resync();
}
@Test
public void methodIsSelectedForDebuggingWithMethodToPrintReturnsTrueIfMethodOfContextMatches() throws Exception {
String methodName = "emptyStaticMethodWithoutAnnotations";
NormalMethod nm = getNormalMethodForTest(methodName);
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = buildOptionsWithMethodToPrintOptionSet(methodName);
InlineOracle io = new DefaultInlineOracle();
GenerationContext gc = new GenerationContext(nm, null, cm, opts, io);
assertThat(gc.methodIsSelectedForDebuggingWithMethodToPrint(), is(true));
}
@Test
public void methodIsSelectedForDebuggingWithMethodToPrintReturnsTrueIfOutermostParentMatches() throws Exception {
String methodName = "emptyStaticMethodWithoutAnnotations";
NormalMethod nm = getNormalMethodForTest(methodName);
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = buildOptionsWithMethodToPrintOptionSet(methodName);
InlineOracle io = new DefaultInlineOracle();
GenerationContext gc = new GenerationContext(nm, null, cm, opts, io);
ExceptionHandlerBasicBlockBag ebag = getMockEbag();
NormalMethod callee = getNormalMethodForTest("emptyStaticMethodWithNoCheckStoreAnnotation");
Instruction noCheckStoreInstr = buildCallInstructionForStaticMethodWithoutReturn(callee, nm);
GenerationContext nextInnerContext = gc.createChildContext(ebag, callee, noCheckStoreInstr);
NormalMethod nextInnerCallee = getNormalMethodForTest("emptyStaticMethodWithNoNullCheckAnnotation");
Instruction noNullCheckInstr = buildCallInstructionForStaticMethodWithoutReturn(callee, nextInnerCallee);
GenerationContext innermostContext = nextInnerContext.createChildContext(ebag, nextInnerCallee, noNullCheckInstr);
assertThat(innermostContext.methodIsSelectedForDebuggingWithMethodToPrint(), is(true));
}
private OptOptions buildOptionsWithMethodToPrintOptionSet(String methodName) {
OptOptions opts = new OptOptions();
opts.processAsOption("-X:opt:", "method_to_print=" + methodName);
assertThat(opts.hasMETHOD_TO_PRINT(), is(true));
Iterator<String> iterator = opts.getMETHOD_TO_PRINTs();
String methodNameFromOpts = iterator.next();
assertThat(methodNameFromOpts, is(methodName));
return opts;
}
@Test
public void canSaveInformationAboutOSRBarriers() throws Exception {
GenerationContext gc = createMostlyEmptyContext("emptyStaticMethodWithoutAnnotations");
Instruction osrBarrier = createMockOSRBarrier();
Instruction call = createMockCall();
gc.saveOSRBarrierForInst(osrBarrier, call);
Instruction barrier = gc.getOSRBarrierFromInst(call);
assertThat(barrier, is(osrBarrier));
}
@Test
public void childContextsSaveOSRBarrierInformationInOutermostParent() throws Exception {
NormalMethod nm = getNormalMethodForTest("methodForInliningTests");
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = new OptOptions();
InlineOracle io = new DefaultInlineOracle();
GenerationContext outermost = new GenerationContext(nm, null, cm, opts, io);
Class<?>[] classArgs = {Object.class};
NormalMethod callee = getNormalMethodForTest("emptyStaticMethodWithObjectParamAndReturnValue", classArgs);
MethodOperand methOp = MethodOperand.STATIC(callee);
RegisterOperand result = createMockRegisterOperand(TypeReference.JavaLangObject);
Instruction callInstr = Call.create(CALL, result, null, methOp, 1);
RegisterOperand objectParam = createMockRegisterOperand(TypeReference.JavaLangObject);
Call.setParam(callInstr, 0, objectParam);
callInstr.setPosition(new InlineSequence(nm));
ExceptionHandlerBasicBlockBag ebag = getMockEbag();
GenerationContext child = outermost.createChildContext(ebag, callee, callInstr);
Instruction osrBarrier = createMockOSRBarrier();
Instruction call = createMockCall();
child.saveOSRBarrierForInst(osrBarrier, call);
assertThat(outermost.getOSRBarrierFromInst(call), is(osrBarrier));
GenerationContext child2 = child.createChildContext(ebag, callee, callInstr);
Instruction osrBarrier2 = createMockOSRBarrier();
Instruction call2 = createMockCall();
child2.saveOSRBarrierForInst(osrBarrier2, call2);
assertThat(outermost.getOSRBarrierFromInst(call2), is(osrBarrier2));
}
@Test
public void childContextsQueryOSRBarrierInformationViaOutermostParent() throws Exception {
NormalMethod nm = getNormalMethodForTest("methodForInliningTests");
CompiledMethod cm = new OptCompiledMethod(-1, nm);
OptOptions opts = new OptOptions();
InlineOracle io = new DefaultInlineOracle();
GenerationContext outermost = new GenerationContext(nm, null, cm, opts, io);
Class<?>[] classArgs = {Object.class};
NormalMethod callee = getNormalMethodForTest("emptyStaticMethodWithObjectParamAndReturnValue", classArgs);
MethodOperand methOp = MethodOperand.STATIC(callee);
RegisterOperand result = createMockRegisterOperand(TypeReference.JavaLangObject);
Instruction callInstr = Call.create(CALL, result, null, methOp, 1);
RegisterOperand objectParam = createMockRegisterOperand(TypeReference.JavaLangObject);
Call.setParam(callInstr, 0, objectParam);
callInstr.setPosition(new InlineSequence(nm));
ExceptionHandlerBasicBlockBag ebag = getMockEbag();
GenerationContext child = outermost.createChildContext(ebag, callee, callInstr);
Instruction osrBarrier = createMockOSRBarrier();
Instruction call = createMockCall();
child.saveOSRBarrierForInst(osrBarrier, call);
assertThat(outermost.getOSRBarrierFromInst(call), is(osrBarrier));
assertThat(child.getOSRBarrierFromInst(call), is(osrBarrier));
GenerationContext child2 = outermost.createChildContext(ebag, callee, callInstr);
assertThat(child2.getOSRBarrierFromInst(call), is(osrBarrier));
}
private Instruction createMockCall() {
return Call.create(CALL, null, null, null, 0);
}
private Instruction createMockOSRBarrier() {
return OsrBarrier.create(OSR_BARRIER, null, 0);
}
@Test(expected = NullPointerException.class)
public void savingNoLongerWorksAfterOSRBarrierInformationWasDiscarded() throws Exception {
GenerationContext gc = createMostlyEmptyContext("methodForInliningTests");
gc.discardOSRBarrierInformation();
Instruction osrBarrier = createMockOSRBarrier();
Instruction call = createMockCall();
gc.saveOSRBarrierForInst(osrBarrier, call);
}
@Test(expected = NullPointerException.class)
public void getOSRBarrierCausesNPEAfterOSRBarrierInformationWasDiscarded() throws Exception {
GenerationContext gc = createMostlyEmptyContext("methodForInliningTests");
Instruction osrBarrier = createMockOSRBarrier();
Instruction call = createMockCall();
gc.saveOSRBarrierForInst(osrBarrier, call);
gc.discardOSRBarrierInformation();
gc.getOSRBarrierFromInst(call);
}
}