< prev index next >
test/compiler/valhalla/valuetypes/ValueTypeTestBench.java
Print this page
rev 10493 : keep
rev 10504 : value type calling convention
*** 27,37 ****
* @test
* @library /testlibrary /test/lib /compiler/whitebox /
* @build compiler.valhalla.valuetypes.ValueTypeTestBench
* @run main ClassFileInstaller sun.hotspot.WhiteBox
* @run main ClassFileInstaller jdk.test.lib.Platform
! * @run main/othervm -noverify -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* compiler.valhalla.valuetypes.ValueTypeTestBench
*/
package compiler.valhalla.valuetypes;
--- 27,40 ----
* @test
* @library /testlibrary /test/lib /compiler/whitebox /
* @build compiler.valhalla.valuetypes.ValueTypeTestBench
* @run main ClassFileInstaller sun.hotspot.WhiteBox
* @run main ClassFileInstaller jdk.test.lib.Platform
! * @run main/othervm -ea -noverify -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
! * compiler.valhalla.valuetypes.ValueTypeTestBench
! * @run main/othervm -ea -noverify -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
! * -XX:+UnlockExperimentalVMOptions -XX:-ValueTypePassFieldsAsArgs
* compiler.valhalla.valuetypes.ValueTypeTestBench
*/
package compiler.valhalla.valuetypes;
*** 44,56 ****
--- 47,62 ----
import jdk.test.lib.Utils;
import sun.hotspot.WhiteBox;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+ import java.lang.annotation.Repeatable;
import java.lang.reflect.Method;
import java.util.ArrayList;
+ import java.util.Arrays;
import java.util.Hashtable;
+ import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
// Test value types
__ByValue final class MyValue1 {
*** 83,93 ****
return x + y + c + v1.hash() + v2.hash();
}
@DontCompile
public long hashInterpreted() {
! return x + y + c + v1.hash() + v2.hash();
}
}
__ByValue final class MyValue2 {
final int x;
--- 89,99 ----
return x + y + c + v1.hash() + v2.hash();
}
@DontCompile
public long hashInterpreted() {
! return x + y + c + v1.hashInterpreted() + v2.hashInterpreted();
}
}
__ByValue final class MyValue2 {
final int x;
*** 107,116 ****
--- 113,127 ----
@ForceInline
public long hash() {
return x + (b ? 0 : 1) + c;
}
+
+ @DontInline
+ public long hashInterpreted() {
+ return x + (b ? 0 : 1) + c;
+ }
}
public class ValueTypeTestBench {
// Print ideal graph after execution of each test
private static final boolean PRINT_GRAPH = true;
*** 152,162 ****
long result = test2(v);
Asserts.assertEQ(result, hash());
}
// Return incoming value type without accessing fields
! @Test(failOn = ALLOC + LOAD + STORE + TRAP)
public MyValue1 test3(MyValue1 v) {
return v;
}
@DontCompile
--- 163,174 ----
long result = test2(v);
Asserts.assertEQ(result, hash());
}
// Return incoming value type without accessing fields
! @Test(valid = ValueTypePassFieldsAsArgsOn, match = {ALLOC, STORE}, matchCount = {1, 9}, failOn = LOAD + TRAP)
! @Test(valid = ValueTypePassFieldsAsArgsOff, failOn = ALLOC + LOAD + STORE + TRAP)
public MyValue1 test3(MyValue1 v) {
return v;
}
@DontCompile
*** 200,210 ****
Asserts.assertEQ(result, hash());
}
// Create a value type in compiled code and pass it to
// the interpreter via a call.
! @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP)
public long test6() {
MyValue1 v = MyValue1.createInline(rI, rL);
// Pass to interpreter
return v.hashInterpreted();
}
--- 212,223 ----
Asserts.assertEQ(result, hash());
}
// Create a value type in compiled code and pass it to
// the interpreter via a call.
! @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = LOAD + TRAP + ALLOC)
! @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP)
public long test6() {
MyValue1 v = MyValue1.createInline(rI, rL);
// Pass to interpreter
return v.hashInterpreted();
}
*** 245,255 ****
Asserts.assertEQ(test8(true), hash());
Asserts.assertEQ(test8(false), hash(rI + 1, rL + 1));
}
// Merge value types created from two branches
! @Test(match = {ALLOC, STORE}, matchCount = {1, 9}, failOn = LOAD + TRAP)
public MyValue1 test9(boolean b) {
MyValue1 v;
if (b) {
// Value type is not allocated
v = MyValue1.createInline(rI, rL);
--- 258,269 ----
Asserts.assertEQ(test8(true), hash());
Asserts.assertEQ(test8(false), hash(rI + 1, rL + 1));
}
// Merge value types created from two branches
! @Test(valid = ValueTypePassFieldsAsArgsOn, match = {LOAD}, matchCount = {9}, failOn = TRAP + ALLOC + STORE)
! @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC, STORE}, matchCount = {1, 9}, failOn = LOAD + TRAP)
public MyValue1 test9(boolean b) {
MyValue1 v;
if (b) {
// Value type is not allocated
v = MyValue1.createInline(rI, rL);
*** 356,366 ****
Asserts.assertEQ(result, warmup ? 42 + (1000*rI) : hash());
}
// Create a value type in a non-inlined method and then call a
// non-inlined method on that value type.
! @Test(failOn = (ALLOC + LOAD + STORE + TRAP))
public long test14() {
MyValue1 v = MyValue1.createDontInline(rI, rL);
return v.hashInterpreted();
}
--- 370,381 ----
Asserts.assertEQ(result, warmup ? 42 + (1000*rI) : hash());
}
// Create a value type in a non-inlined method and then call a
// non-inlined method on that value type.
! @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = (ALLOC + STORE + TRAP), match = {LOAD}, matchCount = {9})
! @Test(valid = ValueTypePassFieldsAsArgsOff, failOn = (ALLOC + LOAD + STORE + TRAP))
public long test14() {
MyValue1 v = MyValue1.createDontInline(rI, rL);
return v.hashInterpreted();
}
*** 370,380 ****
Asserts.assertEQ(result, hash());
}
// Create a value type in an inlined method and then call a
// non-inlined method on that value type.
! @Test(failOn = (LOAD + TRAP), match = {ALLOC}, matchCount = {1})
public long test15() {
MyValue1 v = MyValue1.createInline(rI, rL);
return v.hashInterpreted();
}
--- 385,396 ----
Asserts.assertEQ(result, hash());
}
// Create a value type in an inlined method and then call a
// non-inlined method on that value type.
! @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = (LOAD + TRAP + ALLOC))
! @Test(valid = ValueTypePassFieldsAsArgsOff, failOn = (LOAD + TRAP), match = {ALLOC}, matchCount = {1})
public long test15() {
MyValue1 v = MyValue1.createInline(rI, rL);
return v.hashInterpreted();
}
*** 413,423 ****
}
// Create a value type in compiled code and pass it to the
// interpreter via a call. The value is live at the first call so
// debug info should include a reference to all its fields.
! @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP)
public long test18() {
MyValue1 v = MyValue1.createInline(rI, rL);
v.hashInterpreted();
return v.hashInterpreted();
}
--- 429,440 ----
}
// Create a value type in compiled code and pass it to the
// interpreter via a call. The value is live at the first call so
// debug info should include a reference to all its fields.
! @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = ALLOC + LOAD + TRAP)
! @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP)
public long test18() {
MyValue1 v = MyValue1.createInline(rI, rL);
v.hashInterpreted();
return v.hashInterpreted();
}
*** 429,439 ****
}
// Create a value type in compiled code and pass it to the
// interpreter via a call. The value type is passed twice but
// should only be allocated once.
! @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP)
public long test19() {
MyValue1 v = MyValue1.createInline(rI, rL);
return sumValue(v, v);
}
--- 446,457 ----
}
// Create a value type in compiled code and pass it to the
// interpreter via a call. The value type is passed twice but
// should only be allocated once.
! @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = ALLOC + LOAD + TRAP)
! @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD + TRAP)
public long test19() {
MyValue1 v = MyValue1.createInline(rI, rL);
return sumValue(v, v);
}
*** 450,465 ****
// Create a value type in compiled code and pass it to the
// interpreter via a call. The value type is live at the uncommon
// trap: verify that deoptimization causes the value type to be
// correctly allocated.
! @Test(match = {ALLOC}, matchCount = {1}, failOn = LOAD)
public long test20(boolean flag) {
MyValue1 v = MyValue1.createInline(rI, rL);
if (flag) {
// uncommon trap
! WHITE_BOX.deoptimizeMethod(tests.get("ValueTypeTestBench::test16"));
}
return v.hashInterpreted();
}
@DontCompile
--- 468,484 ----
// Create a value type in compiled code and pass it to the
// interpreter via a call. The value type is live at the uncommon
// trap: verify that deoptimization causes the value type to be
// correctly allocated.
! @Test(valid = ValueTypePassFieldsAsArgsOn, failOn = LOAD + ALLOC + STORE)
! @Test(valid = ValueTypePassFieldsAsArgsOff, match = {ALLOC}, matchCount = {1}, failOn = LOAD)
public long test20(boolean flag) {
MyValue1 v = MyValue1.createInline(rI, rL);
if (flag) {
// uncommon trap
! WHITE_BOX.deoptimizeMethod(tests.get("ValueTypeTestBench::test20"));
}
return v.hashInterpreted();
}
@DontCompile
*** 498,511 ****
--- 517,737 ----
// Check if value type fields were updated
Asserts.assertEQ(val1.hash(), hash(rI + 1, rL + 1));
Asserts.assertEQ(val2.hash(), MyValue2.createInline(rI + 1, true).hash());
}
+ // Test interpreter to compiled code with various signatures
+ @Test(failOn = ALLOC + STORE + TRAP)
+ public long test22(MyValue2 v) {
+ return v.hash();
+ }
+
+ @DontCompile
+ public void test22_verifier(boolean warmup) {
+ MyValue2 v = MyValue2.createInline(rI, true);
+ long result = test22(v);
+ Asserts.assertEQ(result, v.hashInterpreted());
+ }
+
+ @Test(failOn = ALLOC + STORE + TRAP)
+ public long test23(int i1, MyValue2 v, int i2) {
+ return v.hash() + i1 - i2;
+ }
+
+ @DontCompile
+ public void test23_verifier(boolean warmup) {
+ MyValue2 v = MyValue2.createInline(rI, true);
+ long result = test23(rI, v, 2*rI);
+ Asserts.assertEQ(result, v.hashInterpreted() - rI);
+ }
+
+ @Test(failOn = ALLOC + STORE + TRAP)
+ public long test24(long l1, MyValue2 v, long l2) {
+ return v.hash() + l1 - l2;
+ }
+
+ @DontCompile
+ public void test24_verifier(boolean warmup) {
+ MyValue2 v = MyValue2.createInline(rI, true);
+ long result = test24(rL, v, 2*rL);
+ Asserts.assertEQ(result, v.hashInterpreted() - rL);
+ }
+
+ @Test(failOn = ALLOC + STORE + TRAP)
+ public long test25(int i, MyValue2 v, long l) {
+ return v.hash() + i + l;
+ }
+
+ @DontCompile
+ public void test25_verifier(boolean warmup) {
+ MyValue2 v = MyValue2.createInline(rI, true);
+ long result = test25(rI, v, rL);
+ Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
+ }
+
+ @Test(failOn = ALLOC + STORE + TRAP)
+ public long test26(long l, MyValue2 v, int i) {
+ return v.hash() + i + l;
+ }
+
+ @DontCompile
+ public void test26_verifier(boolean warmup) {
+ MyValue2 v = MyValue2.createInline(rI, true);
+ long result = test26(rL, v, rI);
+ Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
+ }
+
+ @Test(failOn = ALLOC + STORE + TRAP)
+ public long test27(long l, MyValue1 v1, int i, MyValue2 v2) {
+ return v1.hash() + i + l + v2.hash();
+ }
+
+ @DontCompile
+ public void test27_verifier(boolean warmup) {
+ MyValue1 v1 = MyValue1.createDontInline(rI, rL);
+ MyValue2 v2 = MyValue2.createInline(rI, true);
+ long result = test27(rL, v1, rI, v2);
+ Asserts.assertEQ(result, v1.hashInterpreted() + rL + rI + v2.hashInterpreted());
+ }
+
+ // Test compiled code to interpreter with various signatures
+ @DontCompile
+ public long test28_interp(MyValue2 v) {
+ return v.hash();
+ }
+
+ @Test(failOn = ALLOC + STORE + TRAP)
+ public long test28(MyValue2 v) {
+ return test28_interp(v);
+ }
+
+ @DontCompile
+ public void test28_verifier(boolean warmup) {
+ MyValue2 v = MyValue2.createInline(rI, true);
+ long result = test28(v);
+ Asserts.assertEQ(result, v.hashInterpreted());
+ }
+
+ @DontCompile
+ public long test29_interp(int i1, MyValue2 v, int i2) {
+ return v.hash() + i1 - i2;
+ }
+
+ @Test(failOn = ALLOC + STORE + TRAP)
+ public long test29(int i1, MyValue2 v, int i2) {
+ return test29_interp(i1, v, i2);
+ }
+
+ @DontCompile
+ public void test29_verifier(boolean warmup) {
+ MyValue2 v = MyValue2.createInline(rI, true);
+ long result = test29(rI, v, 2*rI);
+ Asserts.assertEQ(result, v.hashInterpreted() - rI);
+ }
+
+ @DontCompile
+ public long test30_interp(long l1, MyValue2 v, long l2) {
+ return v.hash() + l1 - l2;
+ }
+
+ @Test(failOn = ALLOC + STORE + TRAP)
+ public long test30(long l1, MyValue2 v, long l2) {
+ return test30_interp(l1, v, l2);
+ }
+
+ @DontCompile
+ public void test30_verifier(boolean warmup) {
+ MyValue2 v = MyValue2.createInline(rI, true);
+ long result = test30(rL, v, 2*rL);
+ Asserts.assertEQ(result, v.hashInterpreted() - rL);
+ }
+
+ @DontCompile
+ public long test31_interp(int i, MyValue2 v, long l) {
+ return v.hash() + i + l;
+ }
+
+ @Test(failOn = ALLOC + STORE + TRAP)
+ public long test31(int i, MyValue2 v, long l) {
+ return test31_interp(i, v, l);
+ }
+
+ @DontCompile
+ public void test31_verifier(boolean warmup) {
+ MyValue2 v = MyValue2.createInline(rI, true);
+ long result = test31(rI, v, rL);
+ Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
+ }
+
+ @DontCompile
+ public long test32_interp(long l, MyValue2 v, int i) {
+ return v.hash() + i + l;
+ }
+
+ @Test(failOn = ALLOC + STORE + TRAP)
+ public long test32(long l, MyValue2 v, int i) {
+ return test32_interp(l, v, i);
+ }
+
+ @DontCompile
+ public void test32_verifier(boolean warmup) {
+ MyValue2 v = MyValue2.createInline(rI, true);
+ long result = test32(rL, v, rI);
+ Asserts.assertEQ(result, v.hashInterpreted() + rL + rI);
+ }
+
+ @DontCompile
+ public long test33_interp(long l, MyValue1 v1, int i, MyValue2 v2) {
+ return v1.hash() + i + l + v2.hash();
+ }
+
+ @Test(failOn = ALLOC + STORE + TRAP)
+ public long test33(long l, MyValue1 v1, int i, MyValue2 v2) {
+ return test33_interp(l, v1, i, v2);
+ }
+
+ @DontCompile
+ public void test33_verifier(boolean warmup) {
+ MyValue1 v1 = MyValue1.createDontInline(rI, rL);
+ MyValue2 v2 = MyValue2.createInline(rI, true);
+ long result = test33(rL, v1, rI, v2);
+ Asserts.assertEQ(result, v1.hashInterpreted() + rL + rI + v2.hashInterpreted());
+ }
+
+ // test that debug info at a call is correct
+ @DontCompile
+ public long test34_interp(MyValue2 v, boolean flag) {
+ if (flag) {
+ // uncommon trap
+ WHITE_BOX.deoptimizeMethod(tests.get("ValueTypeTestBench::test34"));
+ }
+ return v.hash();
+ }
+
+ @Test(failOn = ALLOC + STORE + TRAP)
+ public long test34(MyValue2 v, boolean flag, long l) {
+ return test34_interp(v, flag) + l;
+ }
+
+ @DontCompile
+ public void test34_verifier(boolean warmup) {
+ MyValue2 v = MyValue2.createInline(rI, true);
+ long result = test34(v, false, rL);
+ Asserts.assertEQ(result, v.hashInterpreted() + rL);
+ if (!warmup) {
+ result = test34(v, true, rL);
+ Asserts.assertEQ(result, v.hashInterpreted() + rL);
+ }
+ }
// ========== Test infrastructure ==========
private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();
+ private static final int ValueTypePassFieldsAsArgsOn = 0x1;
+ private static final int ValueTypePassFieldsAsArgsOff = 0x2;
+ static final int AllFlags = ValueTypePassFieldsAsArgsOn | ValueTypePassFieldsAsArgsOff;
+ private static final boolean ValueTypePassFieldsAsArgs = (Boolean)WHITE_BOX.getVMFlag("ValueTypePassFieldsAsArgs");
private static final int COMP_LEVEL_ANY = -1;
private static final int COMP_LEVEL_FULL_OPTIMIZATION = 4;
private static final Hashtable<String, Method> tests = new Hashtable<String, Method>();
private static final int WARMUP = 10;
private static boolean USE_COMPILER = WHITE_BOX.getBooleanVMFlag("UseCompiler");
*** 528,554 ****
public static final long rL = Utils.getRandomInstance().nextLong() % 1000;
static {
// Gather all test methods and put them in Hashtable
for (Method m : ValueTypeTestBench.class.getDeclaredMethods()) {
! if (m.isAnnotationPresent(Test.class)) {
tests.put("ValueTypeTestBench::" + m.getName(), m);
}
}
}
public static void main(String[] args) throws Throwable {
if (args.length == 0) {
! // Run tests in own process and verify output
! OutputAnalyzer oa = ProcessTools.executeTestJvm("-noverify",
"-XX:+UnlockDiagnosticVMOptions", "-Xbootclasspath/a:.", "-XX:+WhiteBoxAPI",
"-XX:-TieredCompilation", "-XX:-BackgroundCompilation", "-XX:-UseOnStackReplacement",
"-XX:+IgnoreUnrecognizedVMOptions", "-XX:+PrintCompilation", "-XX:+PrintIdeal", "-XX:+PrintOptoAssembly",
"-XX:CompileCommand=quiet", "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.ValueTypeTestBench::*",
"-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue1::*",
! "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue2::*",
! ValueTypeTestBench.class.getName(), "run");
// If ideal graph printing is enabled/supported, verify output
String output = oa.getOutput();
oa.shouldHaveExitValue(0);
if (output.contains("PrintIdeal enabled")) {
parseOutput(output);
--- 754,791 ----
public static final long rL = Utils.getRandomInstance().nextLong() % 1000;
static {
// Gather all test methods and put them in Hashtable
for (Method m : ValueTypeTestBench.class.getDeclaredMethods()) {
! Test[] annos = m.getAnnotationsByType(Test.class);
! if (annos.length != 0) {
tests.put("ValueTypeTestBench::" + m.getName(), m);
}
}
}
public static void main(String[] args) throws Throwable {
if (args.length == 0) {
! ArrayList<String> all_args = new ArrayList(List.of(
! "-noverify",
"-XX:+UnlockDiagnosticVMOptions", "-Xbootclasspath/a:.", "-XX:+WhiteBoxAPI",
"-XX:-TieredCompilation", "-XX:-BackgroundCompilation", "-XX:-UseOnStackReplacement",
"-XX:+IgnoreUnrecognizedVMOptions", "-XX:+PrintCompilation", "-XX:+PrintIdeal", "-XX:+PrintOptoAssembly",
"-XX:CompileCommand=quiet", "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.ValueTypeTestBench::*",
"-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue1::*",
! "-XX:CompileCommand=compileonly,compiler.valhalla.valuetypes.MyValue2::*"
! ));
! // Run tests in own process and verify output
! all_args.add("-XX:+UnlockExperimentalVMOptions");
! if ((Boolean)WHITE_BOX.getVMFlag("ValueTypePassFieldsAsArgs")) {
! all_args.add("-XX:+ValueTypePassFieldsAsArgs");
! } else {
! all_args.add("-XX:-ValueTypePassFieldsAsArgs");
! }
! all_args.add(ValueTypeTestBench.class.getName());
! all_args.add("run");
! OutputAnalyzer oa = ProcessTools.executeTestJvm(all_args.toArray(new String[0]));
// If ideal graph printing is enabled/supported, verify output
String output = oa.getOutput();
oa.shouldHaveExitValue(0);
if (output.contains("PrintIdeal enabled")) {
parseOutput(output);
*** 581,591 ****
}
if (PRINT_GRAPH) {
System.out.println("\nGraph for " + graph);
}
// Parse graph using regular expressions to determine if it contains forbidden nodes
! Test anno = test.getAnnotation(Test.class);
String regexFail = anno.failOn();
if (!regexFail.isEmpty()) {
Pattern pattern = Pattern.compile(regexFail.substring(0, regexFail.length()-1));
Matcher matcher = pattern.matcher(graph);
boolean fail = false;
--- 818,839 ----
}
if (PRINT_GRAPH) {
System.out.println("\nGraph for " + graph);
}
// Parse graph using regular expressions to determine if it contains forbidden nodes
! Test[] annos = test.getAnnotationsByType(Test.class);
! Test anno = null;
! for (Test a : annos) {
! if ((a.valid() & ValueTypePassFieldsAsArgsOn) != 0 && ValueTypePassFieldsAsArgs) {
! assert anno == null;
! anno = a;
! } else if ((a.valid() & ValueTypePassFieldsAsArgsOff) != 0 && !ValueTypePassFieldsAsArgs) {
! assert anno == null;
! anno = a;
! }
! }
! assert anno != null;
String regexFail = anno.failOn();
if (!regexFail.isEmpty()) {
Pattern pattern = Pattern.compile(regexFail.substring(0, regexFail.length()-1));
Matcher matcher = pattern.matcher(graph);
boolean fail = false;
*** 665,681 ****
--- 913,936 ----
}
}
// Mark method as test
@Retention(RetentionPolicy.RUNTIME)
+ @Repeatable(Tests.class)
@interface Test {
// Regular expression used to match forbidden IR nodes
// in the C2 IR emitted for this test.
String failOn() default "";
// Regular expressions used to match and count IR nodes.
String[] match() default { };
int[] matchCount() default { };
+ int valid() default ValueTypeTestBench.AllFlags;
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface Tests {
+ Test[] value();
}
// Force method inlining during compilation
@Retention(RetentionPolicy.RUNTIME)
@interface ForceInline { }
< prev index next >