/* * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 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.aarch64; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Method; import jdk.test.lib.process.OutputAnalyzer; import jdk.test.lib.process.ProcessTools; import sun.hotspot.WhiteBox; import sun.hotspot.cpuinfo.CPUInfo; import compiler.whitebox.CompilerWhiteBoxTest; public class CodegenTestRunner { private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) protected @interface CallMath {} @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) protected @interface CheckOpto { String neon() default ""; String sve() default ""; String sve2() default ""; } private void checkMethod(Method method, int sveLevel) throws Exception { String className = getClass().getName(); String methodName = method.getName(); // Check if test method is valid if (method.getParameterCount() > 0) { throw new RuntimeException("Test method should have zero parameter"); } // Find expected NEON/SVE instructions from the annotation CheckOpto optos = method.getAnnotation(CheckOpto.class); String opcodes, suffix; switch (sveLevel) { case 0: opcodes = optos.neon(); suffix = "# vector \\([0-9]+[A-Z]+\\)"; // 16B, 8H, 4S, 2D, ... break; case 1: opcodes = optos.sve(); suffix = "\\(sve\\)"; break; case 2: opcodes = optos.sve2(); suffix = "\\(sve2\\)"; break; default: throw new RuntimeException("Unexpected SVE level"); } // Skip this check if the expected instruction is not defined if (opcodes.equals("")) { return; } // Create a process to compile specified method String[] procArgs = { "-Xbootclasspath/a:.", "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+WhiteBoxAPI", "-XX:-BackgroundCompilation", "-XX:+PrintOptoAssembly", "-XX:UseSVE=" + sveLevel, "-XX:CompileOnly=" + className + "::" + methodName, className, methodName }; ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(procArgs); OutputAnalyzer output = new OutputAnalyzer(pb.start()); // The sub-process should exit with no error output.shouldHaveExitValue(0); output.stderrShouldBeEmptyIgnoreVMWarnings(); // Pattern match of the printed OptoAssembly to check the existence of // expected SIMD instructions // E.g. // 27c sve_add V16, V17, V16 # vector (sve) // matches // "{SPACES}{opc}{SPACES}{ANYTHING}{suffix}" for (String opc : opcodes.split("\\|")) { // Check comma-separated opto instructions String regex = "\\s+" + opc + "\\s+.+" + suffix; output.stdoutShouldMatch(regex); } } private void testMethodsWithSVELevel(int sveLevel) { Class testClass = getClass(); for (Method testMethod : testClass.getDeclaredMethods()) { // Check all methods with CheckOpto annotation if (testMethod.isAnnotationPresent(CheckOpto.class)) { try { checkMethod(testMethod, sveLevel); } catch (Exception e) { // Report failed case and exit System.err.println("Test failed in " + testClass.getName() + "." + testMethod.getName() + ": " + e.getMessage()); System.exit(1); } } } } private void checkCPUFeatureAndRun() { if (CPUInfo.hasFeature("sve2")) { testMethodsWithSVELevel(2); } if (CPUInfo.hasFeature("sve")) { testMethodsWithSVELevel(1); } // always check NEON testMethodsWithSVELevel(0); } private void compileMethodByName(String methodName) { try { Method method = getClass().getDeclaredMethod(methodName); if (method.isAnnotationPresent(CallMath.class)) { // For method that calls intrinsified math methods, the class // java.lang.Math should be loaded before the compilation to // avoid C2 generating unexpected uncommon traps try { Class.forName("java.lang.Math"); } catch (ClassNotFoundException e) { System.err.println("Failed to load class java.lang.Math"); } } WHITE_BOX.enqueueMethodForCompilation( method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); } catch (NoSuchMethodException e) { System.err.println("Method " + methodName + " not found"); } } protected void run(String[] args) { if (args.length == 0) { // We are in the main java process running the test runner // Create sub-processes to run the case with all supported SVE level checkCPUFeatureAndRun(); } else if (args.length == 1) { // We are in the forked java process running the case // Compile the method specified by args[0] for SIMD instruction check compileMethodByName(args[0]); } else { System.err.println("Unexpected args[] length"); System.exit(1); } } }