/*
* 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.
*/
import java.lang.reflect.Method;
import org.jikesrvm.VM;
import org.jikesrvm.classloader.RVMMethod;
/**
* A test to detect regressions in the quality of the optimizing compiler's
* inline allocation sequence that is generated by inlining
* {@link org.jikesrvm.runtime.RuntimeEntrypoints#resolvedNewScalar(int, org.jikesrvm.objectmodel.TIB, boolean, int, int, int, int)} and
* {@link org.jikesrvm.runtime.RuntimeEntrypoints#resolvedNewArray(int, int, int, org.jikesrvm.objectmodel.TIB, int, int, int, int)}
*
* This test will only run on Jikes RVM as it reaches into the
* internals of the VM to get access to the generated machine code for
* the prototypical methods.
*
* It can also be used with org.jikesrvm.tools.oth.OptTestHarness to manually inspect and
* tune the inline allocation sequence.
*/
class InlineAllocation {
int a;
int b;
// We allow some pork for assertion checking in the inline allocation sequence,
// However, we can't allow this to get out of hand...otherwise builds take way too long.
static int assertionSpace = VM.VerifyAssertions ? (VM.BuildForIA32 ? 40 : 20) : 0;
// Limits on sizes of allocation sequences.
// Make them specific to the allocator so we can make them reasonably tight.
static int alloc1Limit = assertionSpace + (VM.BuildForIA32 ? 100 : 30); // small object
static int alloc2Limit = alloc1Limit + (VM.BuildForIA32 ? 8 : 2); // small array. Should be only the store of the length different than small object
static int alloc3Limit = assertionSpace + (VM.BuildForIA32 ? 100 : 30); // large object
static int alloc4Limit = (VM.BuildForIA32 ? 40 : 11); // unknown size object. Should not be inlined at all.
// The limits are either in bytes or number of instructions, depending on the architecture
static final String unit = VM.BuildForIA32 ? " bytes" : " instructions";
/**
* A trivial method that should require the full prologue/epilogue
* sequence (except that it won't use any nonvolatile registers...sigh).
* This enables us to subtract off the expected prologue/epilogue size
* from the machine code size of the other methods.
*/
int trivial(InlineAllocation x) {
return x.a;
}
/**
* Allocation a scalar
*/
InlineAllocation alloc1() {
return new InlineAllocation();
}
/**
* Allocate an array of small, constant size
*/
int[] alloc2() {
return new int[10];
}
/**
* Allocate an array of large, constant size
*/
int[] alloc3() {
return new int[100000];
}
/**
* Allocate an array of unknown size
*/
int [] alloc4(int size) {
return new int[size];
}
/**
* Force compilation of each of the methods and report on the size
* of the generated machine code.
*/
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("InlineAllocation");
Method trivialJ = clazz.getDeclaredMethod("trivial", new Class[] {clazz});
Method alloc1J = clazz.getDeclaredMethod("alloc1", (Class[])null);
Method alloc2J = clazz.getDeclaredMethod("alloc2", (Class[])null);
Method alloc3J = clazz.getDeclaredMethod("alloc3", (Class[])null);
Method alloc4J = clazz.getDeclaredMethod("alloc4", new Class[] {Integer.TYPE});
RVMMethod trivial = java.lang.reflect.JikesRVMSupport.getMethodOf(trivialJ);
RVMMethod alloc1 = java.lang.reflect.JikesRVMSupport.getMethodOf(alloc1J);
RVMMethod alloc2 = java.lang.reflect.JikesRVMSupport.getMethodOf(alloc2J);
RVMMethod alloc3 = java.lang.reflect.JikesRVMSupport.getMethodOf(alloc3J);
RVMMethod alloc4 = java.lang.reflect.JikesRVMSupport.getMethodOf(alloc4J);
trivial.compile();
int trivialSize = trivial.getCurrentCompiledMethod().numberOfInstructions();
alloc1.compile();
int alloc1Size = alloc1.getCurrentCompiledMethod().numberOfInstructions();
alloc2.compile();
int alloc2Size = alloc2.getCurrentCompiledMethod().numberOfInstructions();
alloc3.compile();
int alloc3Size = alloc3.getCurrentCompiledMethod().numberOfInstructions();
alloc4.compile();
int alloc4Size = alloc4.getCurrentCompiledMethod().numberOfInstructions();
// System.out.println("Trivial method is "+trivialSize);
// System.out.println("Scalar allocation size is "+alloc1Size);
// System.out.println("Small array allocation is "+alloc2Size);
// System.out.println("Large array allocation is "+alloc3Size);
// System.out.println("Unknown size array allocation is "+alloc4Size);
// Subtract off prologue/epilogue size. This is approximate!!!
// If you really care, count the instructions by hand!
alloc1Size -= trivialSize;
alloc2Size -= trivialSize;
alloc3Size -= trivialSize;
alloc4Size -= trivialSize;
System.out.println("Approximate scalar allocation size is " + alloc1Size + unit);
System.out.println("Approximate small array allocation is " + alloc2Size + unit);
System.out.println("Approximate large array allocation is " + alloc3Size + unit);
System.out.println("Approximate unknown size array allocation is " + alloc4Size + unit);
boolean fail = false;
if (alloc1Size > alloc1Limit) {
System.out.println("FAIL: scalar allocation is too porky");
printLimit(alloc1Limit);
fail = true;
}
if (alloc2Size > alloc2Limit) {
System.out.println("FAIL: small array allocation is too porky");
printLimit(alloc2Limit);
fail = true;
}
if (alloc3Size > alloc3Limit) {
System.out.println("FAIL: large array allocation is too porky");
printLimit(alloc3Limit);
fail = true;
}
if (alloc4Size > alloc4Limit) {
System.out.println("FAIL: unknown size array allocation is too porky");
printLimit(alloc4Limit);
fail = true;
}
if (!fail) {
System.out.println("ALL TESTS PASSED");
}
}
private static void printLimit(int limit) {
System.out.println(" limit is " + limit + unit);
}
}