/* * Copyright (c) 2022, Arm Limited. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package compiler.vectorization.runner; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.io.File; import jdk.test.lib.Utils; import sun.hotspot.WhiteBox; public class VectorizationTestRunner { private static final WhiteBox WB = WhiteBox.getWhiteBox(); private static final int COMP_LEVEL_INTP = 0; private static final int COMP_LEVEL_C2 = 4; private static final int NMETHOD_COMP_LEVEL_IDX = 1; private static final int NMETHOD_INSTS_IDX = 2; private static final long COMP_THRES_SECONDS = 30; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) protected @interface Test {} protected void run() { // Add extra VM options to enable post loop vectorization WB.setBooleanVMFlag("UnlockExperimentalVMOptions", true); WB.setBooleanVMFlag("PostLoopMultiversioning", true); // For each method annotated with @Test in the test method, this test runner // invokes it twice - first time in the interpreter and second time compiled // by C2. Then this runner compares the two return values. Hence we require // each test method returning a primitive value or an array of primitive type. // And each test method should not throw any exceptions. Class klass = getClass(); for (Method method : klass.getDeclaredMethods()) { try { if (method.isAnnotationPresent(Test.class)) { verifyTestMethod(method); runTestOnMethod(method); } } catch (Exception e) { throw new RuntimeException("Test failed in " + klass.getName() + "." + method.getName() + ": " + e.getMessage()); } } } private void verifyTestMethod(Method method) { // Check method parameter count if (method.getParameterCount() > 0) { fail("Test method should have no parameter."); } // Check method modifiers int modifiers = method.getModifiers(); if (!Modifier.isPublic(modifiers) || Modifier.isStatic(modifiers)) { fail("Test method should be public and non-static."); } // Check method return type Class retType = method.getReturnType(); if (retType.isPrimitive()) { if (retType == Void.TYPE) { fail("Test method should return non-void."); } } else if (retType.isArray()) { Class elemType = retType.getComponentType(); if (!elemType.isPrimitive()) { fail("Only primitive array types are supported."); } } else { fail("Test method should not return Object type."); } } private void runTestOnMethod(Method method) throws InterruptedException { Object expected = null; Object actual = null; // Lock compilation and inovke the method to get reference result from // the interpreter WB.lockCompilation(); try { expected = method.invoke(this); } catch (Exception e) { e.printStackTrace(); fail("Exception is thrown in test method invocation (interpreter)."); } assert(WB.getMethodCompilationLevel(method) == COMP_LEVEL_INTP); WB.unlockCompilation(); // Compile the method and invoke it again long enqueueTime = System.currentTimeMillis(); WB.enqueueMethodForCompilation(method, COMP_LEVEL_C2); while (WB.getMethodCompilationLevel(method) != COMP_LEVEL_C2) { if (System.currentTimeMillis() - enqueueTime > COMP_THRES_SECONDS * 1000) { fail("Method is not compiled after " + COMP_THRES_SECONDS + "s."); } Thread.sleep(50 /*ms*/); } try { actual = method.invoke(this); } catch (Exception e) { e.printStackTrace(); fail("Exception is thrown in test method invocation (C2)."); } assert(WB.getMethodCompilationLevel(method) == COMP_LEVEL_C2); // Check if two invocations return the same Class retType = method.getReturnType(); if (retType.isArray()) { // Method invocations from Java reflection API always return a boxed object. // Hence, for methods return primitive array, we can only use reflection API // to check the consistency of the elements one by one. if (expected == null && actual == null) { return; } if (expected == null ^ actual == null) { fail("Inconsistent return value: null/non-null."); } int length = Array.getLength(expected); if (Array.getLength(actual) != length) { fail("Inconsistent array length: expected = " + length + ", actual = " + Array.getLength(actual)); } for (int idx = 0; idx < length; idx++) { Object e1 = Array.get(expected, idx); Object e2 = Array.get(actual, idx); if (!e1.equals(e2)) { fail("Inconsistent value at array index [" + idx + "], expected = " + e1 + ", actual = " + e2); } } } else { // Method invocations from Java reflection API always return a boxed object. // Hence, we should use equals() to check the consistency for methods which // return primitive type. if (!expected.equals(actual)) { fail("Inconsistent return value: expected = " + expected + ", actual = " + actual); } } } private static VectorizationTestRunner createTestInstance(String testName) { if (!testName.toLowerCase().endsWith(".java")) { fail("Invalid test file name " + testName); } testName = testName.substring(0, testName.length() - 5); testName = testName.replace('/', '.'); VectorizationTestRunner instance = null; try { Class klass = Class.forName(testName); Constructor ctor = klass.getConstructor(); instance = (VectorizationTestRunner) ctor.newInstance(); } catch (Exception e) { e.printStackTrace(); fail("Cannot create test instance for class " + testName); } return instance; } private static void fail(String reason) { throw new RuntimeException(reason); } public static void main(String[] args) { VectorizationTestRunner testObj = createTestInstance(Utils.TEST_NAME); testObj.run(); } }