1 /*
  2  * Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 package compiler.intrinsics.bmi;
 25 
 26 import jdk.test.lib.Asserts;
 27 import jdk.test.lib.process.OutputAnalyzer;
 28 import jdk.test.lib.process.ProcessTools;
 29 import jdk.test.lib.Utils;
 30 
 31 import java.io.IOException;
 32 import java.nio.file.Files;
 33 import java.nio.file.Paths;
 34 import java.util.Collections;
 35 import java.util.LinkedList;
 36 import java.util.List;
 37 import java.util.Random;
 38 
 39 /**
 40  * Test runner that invokes all methods implemented by particular Expr
 41  * with random arguments in two different JVM processes and compares output.
 42  * JVMs being started in different modes - one in int and other in comp
 43  * with C2 and disabled tiered compilation.
 44  */
 45 public class BMITestRunner {
 46 
 47     enum VMMode {
 48         COMP, INT;
 49     };
 50 
 51     public static int DEFAULT_ITERATIONS_COUNT = 4000;
 52 
 53     /**
 54      * Execute all methods implemented by <b>expr</b> in int and comp modes
 55      * and compare output.
 56      * Test pass only of output obtained with different VM modes is equal.
 57      * To control behaviour of test following options could be passed:
 58      * <ul>
 59      *   <li>-iterations=&lt;N&gt; each operation implemented by
 60      *       <b>expr</b> will be executed <i>N</i> times. Default value
 61      *       is 4000.</li>
 62      * </ul>
 63      *
 64      * @param expr operation that should be tested
 65      * @param testOpts options to control test behaviour
 66      * @param additionalVMOpts additional options for VM
 67      *
 68      * @throws Throwable if test failed.
 69      */
 70     public static void runTests(Class<? extends Expr> expr,
 71                                 String testOpts[],
 72                                 String... additionalVMOpts)
 73                          throws Throwable {
 74 
 75         // ensure seed got printed out
 76         Utils.getRandomInstance();
 77         long seed = Utils.SEED;
 78         int iterations = DEFAULT_ITERATIONS_COUNT;
 79 
 80         for (String testOption : testOpts) {
 81             if (testOption.startsWith("-iterations=")) {
 82                 iterations = Integer.valueOf(testOption.
 83                                              replace("-iterations=", ""));
 84             }
 85         }
 86 
 87         OutputAnalyzer intOutput = runTest(expr, VMMode.INT,
 88                                            additionalVMOpts,
 89                                            seed, iterations);
 90         OutputAnalyzer compOutput = runTest(expr, VMMode.COMP,
 91                                             additionalVMOpts,
 92                                             seed, iterations);
 93 
 94         dumpOutput(intOutput, "int");
 95         dumpOutput(compOutput, "comp");
 96 
 97         Asserts.assertStringsEqual(intOutput.getStdout(),
 98                                    compOutput.getStdout(),
 99                                    "Results obtained in -Xint and " +
100                                    "-Xcomp should be the same.");
101     }
102 
103     /**
104      * Execute tests on methods implemented by <b>expr</b> in new VM
105      * started in <b>testVMMode</b> mode.
106      *
107      * @param expr operation that should be tested
108      * @param testVMMode VM mode for test
109      * @param additionalVMOpts additional options for VM
110      * @param seed for RNG used it tests
111      * @param iterations that will be used to invoke <b>expr</b>'s methods.
112      *
113      * @return OutputAnalyzer for executed test.
114      * @throws Throwable when something goes wrong.
115      */
116     public static OutputAnalyzer runTest(Class<? extends Expr> expr,
117                                          VMMode testVMMode,
118                                          String additionalVMOpts[],
119                                          long seed, int iterations)
120                                   throws Throwable {
121 
122         List<String> vmOpts = new LinkedList<String>();
123 
124         Collections.addAll(vmOpts, additionalVMOpts);
125         // Hide timestamps from warnings (e.g. due to potential AOT
126         // saved/runtime state mismatch), to avoid false positives when
127         // comparing output across runs.
128         vmOpts.add("-Xlog:all=warning,aot=off:stdout:level,tags");
129 
130         //setup mode-specific options
131         switch (testVMMode) {
132         case INT:
133             Collections.addAll(vmOpts, new String[] { "-Xint" });
134             break;
135         case COMP:
136             Collections.addAll(vmOpts, new String[] {
137                     "-Xcomp",
138                     "-XX:-TieredCompilation",
139                     String.format("-XX:CompileCommand=compileonly,%s::*",
140                                   expr.getName())
141                 });
142             break;
143         }
144 
145         Collections.addAll(vmOpts, new String[] {
146                 "-XX:+DisplayVMOutputToStderr",
147                 "-D" + Utils.SEED_PROPERTY_NAME + "=" + seed,
148                 Executor.class.getName(),
149                 expr.getName(),
150                 new Integer(iterations).toString()
151             });
152 
153         OutputAnalyzer outputAnalyzer = ProcessTools.executeTestJava(vmOpts);
154 
155         outputAnalyzer.shouldHaveExitValue(0);
156 
157         return outputAnalyzer;
158     }
159 
160     /**
161      * Dump stdout and stderr of test process to <i>prefix</i>.test.out
162      * and <i>prefix</i>.test.err respectively.
163      *
164      * @param outputAnalyzer OutputAnalyzer whom output should be dumped
165      * @param prefix Prefix that will be used in file names.
166      * @throws IOException if unable to dump output to file.
167      */
168     protected static void dumpOutput(OutputAnalyzer outputAnalyzer,
169                                      String prefix)
170                               throws IOException {
171         Files.write(Paths.get(prefix + ".test.out"),
172                     outputAnalyzer.getStdout().getBytes());
173 
174         Files.write(Paths.get(prefix + ".test.err"),
175                     outputAnalyzer.getStderr().getBytes());
176     }
177 
178 
179     /**
180      * Executor that invoke all methods implemented by particular
181      * Expr instance.
182      */
183     public static class Executor {
184 
185         /**
186          * Usage: BMITestRunner$Executor &lt;ExprClassName&gt; &lt;iterations&gt;
187          */
188         public static void main(String args[]) throws Exception {
189             @SuppressWarnings("unchecked")
190             Class<? extends Expr> exprClass =
191                 (Class<? extends Expr>)Class.forName(args[0]);
192             Expr expr = exprClass.getConstructor().newInstance();
193             int iterations = Integer.valueOf(args[1]);
194             runTests(expr, iterations, Utils.getRandomInstance());
195         }
196 
197 
198         public static int[] getIntBitShifts() {
199             //SIZE+1 shift is for zero.
200             int data[] = new int[Integer.SIZE+1];
201             for (int s = 0; s < data.length; s++) {
202                 data[s] = 1<<s;
203             }
204             return data;
205         }
206 
207         public static long[] getLongBitShifts() {
208             //SIZE+1 shift is for zero.
209             long data[] = new long[Long.SIZE+1];
210             for (int s = 0; s < data.length; s++) {
211                 data[s] = 1L<<s;
212             }
213             return data;
214         }
215 
216         public static void log(String format, Object... args) {
217             System.out.println(String.format(format, args));
218         }
219 
220         public static void runTests(Expr expr, int iterations, Random rng) {
221             runUnaryIntRegTest(expr, iterations, rng);
222             runUnaryIntMemTest(expr, iterations, rng);
223             runUnaryLongRegTest(expr, iterations, rng);
224             runUnaryLongMemTest(expr, iterations, rng);
225             runUnaryIntToLongRegTest(expr, iterations, rng);
226             runBinaryRegRegIntTest(expr, iterations, rng);
227             runBinaryRegMemIntTest(expr, iterations, rng);
228             runBinaryMemRegIntTest(expr, iterations, rng);
229             runBinaryMemMemIntTest(expr, iterations, rng);
230             runBinaryRegRegLongTest(expr, iterations, rng);
231             runBinaryRegMemLongTest(expr, iterations, rng);
232             runBinaryMemRegLongTest(expr, iterations, rng);
233             runBinaryMemMemLongTest(expr, iterations, rng);
234         }
235 
236         public static void runUnaryIntRegTest(Expr expr, int iterations,
237                                               Random rng) {
238             if (!(expr.isUnaryArgumentSupported()
239                   && expr.isIntExprSupported())) {
240                 return;
241             }
242 
243             for (int value : getIntBitShifts()) {
244                 log("UnaryIntReg(0X%x) -> 0X%x",
245                     value, expr.intExpr(value));
246             }
247 
248             for (int i = 0; i < iterations; i++) {
249                 int value = rng.nextInt();
250                 log("UnaryIntReg(0X%x) -> 0X%x",
251                     value, expr.intExpr(value));
252             }
253         }
254 
255         public static void runUnaryIntMemTest(Expr expr, int iterations,
256                                               Random rng) {
257             if (!(expr.isUnaryArgumentSupported()
258                   && expr.isIntExprSupported()
259                   && expr.isMemExprSupported())) {
260                 return;
261             }
262 
263             for (int value : getIntBitShifts()) {
264                 log("UnaryIntMem(0X%x) -> 0X%x",
265                     value, expr.intExpr(new Expr.MemI(value)));
266             }
267 
268             for (int i = 0; i < iterations; i++) {
269                 int value = rng.nextInt();
270                 log("UnaryIntMem(0X%x) -> 0X%x",
271                     value, expr.intExpr(new Expr.MemI(value)));
272             }
273         }
274 
275         public static void runUnaryLongRegTest(Expr expr, int iterations,
276                                                Random rng) {
277             if (!(expr.isUnaryArgumentSupported()
278                   && expr.isLongExprSupported())) {
279                 return;
280             }
281 
282             for (long value : getLongBitShifts()) {
283                 log("UnaryLongReg(0X%x) -> 0X%x",
284                     value, expr.longExpr(value));
285             }
286 
287             for (int i = 0; i < iterations; i++) {
288                 long value = rng.nextLong();
289                 log("UnaryLongReg(0X%x) -> 0X%x",
290                     value, expr.longExpr(value));
291             }
292         }
293 
294         public static void runUnaryLongMemTest(Expr expr, int iterations,
295                                                Random rng) {
296             if (!(expr.isUnaryArgumentSupported()
297                   && expr.isLongExprSupported()
298                   && expr.isMemExprSupported())) {
299                 return;
300             }
301 
302             for (long value : getLongBitShifts()) {
303                 log("UnaryLongMem(0X%x) -> 0X%x",
304                     value, expr.longExpr(new Expr.MemL(value)));
305             }
306 
307             for (int i = 0; i < iterations; i++) {
308                 long value = rng.nextLong();
309                 log("UnaryLongMem(0X%x) -> 0X%x",
310                     value, expr.longExpr(new Expr.MemL(value)));
311             }
312         }
313 
314         public static void runUnaryIntToLongRegTest(Expr expr, int iterations,
315                                                     Random rng) {
316             if (!(expr.isUnaryArgumentSupported()
317                   && expr.isIntToLongExprSupported())) {
318                 return;
319             }
320 
321             for (int value : getIntBitShifts()) {
322                 log("UnaryIntToLongReg(0X%x) -> 0X%x",
323                     value, expr.intToLongExpr(value));
324             }
325 
326             for (int i = 0; i < iterations; i++) {
327                 int value = rng.nextInt();
328                 log("UnaryIntToLongReg(0X%x) -> 0X%x",
329                     value, expr.intToLongExpr(value));
330             }
331         }
332 
333         public static void runBinaryRegRegIntTest(Expr expr, int iterations,
334                                                   Random rng) {
335             if (!(expr.isIntExprSupported()
336                   && expr.isBinaryArgumentSupported())) {
337                 return;
338             }
339 
340             for (int i = 0; i < iterations; i++) {
341                 int aValue = rng.nextInt();
342                 int bValue = rng.nextInt();
343                 log("BinaryIntRegReg(0X%x, 0X%x) -> 0X%x",
344                     aValue, bValue, expr.intExpr(aValue, bValue));
345             }
346         }
347 
348         public static void runBinaryRegMemIntTest(Expr expr, int iterations,
349                                                   Random rng) {
350             if (!(expr.isIntExprSupported()
351                   && expr.isBinaryArgumentSupported()
352                   && expr.isMemExprSupported())) {
353                 return;
354             }
355 
356             for (int i = 0; i < iterations; i++) {
357                 int aValue = rng.nextInt();
358                 int bValue = rng.nextInt();
359                 log("BinaryIntRegMem(0X%x, 0X%x) -> 0X%x", aValue, bValue,
360                     expr.intExpr(aValue, new Expr.MemI(bValue)));
361             }
362         }
363 
364         public static void runBinaryMemRegIntTest(Expr expr, int iterations,
365                                                   Random rng) {
366             if (!(expr.isIntExprSupported()
367                   && expr.isBinaryArgumentSupported()
368                   && expr.isMemExprSupported())) {
369                 return;
370             }
371 
372             for (int i = 0; i < iterations; i++) {
373                 int aValue = rng.nextInt();
374                 int bValue = rng.nextInt();
375                 log("BinaryIntMemReg(0X%x, 0X%x) -> 0X%x", aValue, bValue,
376                     expr.intExpr(new Expr.MemI(aValue), bValue));
377             }
378         }
379 
380         public static void runBinaryMemMemIntTest(Expr expr, int iterations,
381                                                   Random rng) {
382             if (!(expr.isIntExprSupported()
383                   && expr.isBinaryArgumentSupported()
384                   && expr.isMemExprSupported())) {
385                 return;
386             }
387 
388             for (int i = 0; i < iterations; i++) {
389                 int aValue = rng.nextInt();
390                 int bValue = rng.nextInt();
391                 log("BinaryIntMemMem(0X%x, 0X%x) -> 0X%x", aValue, bValue,
392                     expr.intExpr(new Expr.MemI(aValue),
393                                  new Expr.MemI(bValue)));
394             }
395         }
396 
397         public static void runBinaryRegRegLongTest(Expr expr,
398                                                    int iterations,
399                                                    Random rng) {
400             if (!(expr.isLongExprSupported()
401                   && expr.isBinaryArgumentSupported())) {
402                 return;
403             }
404 
405             for (int i = 0; i < iterations; i++) {
406                 long aValue = rng.nextLong();
407                 long bValue = rng.nextLong();
408                 log("BinaryLongRegReg(0X%x, 0X%x) -> 0X%x", aValue, bValue,
409                     expr.longExpr(aValue, bValue));
410             }
411         }
412 
413         public static void runBinaryRegMemLongTest(Expr expr,
414                                                    int iterations,
415                                                    Random rng) {
416             if (!(expr.isLongExprSupported()
417                   && expr.isBinaryArgumentSupported()
418                   && expr.isMemExprSupported())) {
419                 return;
420             }
421 
422             for (int i = 0; i < iterations; i++) {
423                 long aValue = rng.nextLong();
424                 long bValue = rng.nextLong();
425                 log("BinaryLongRegMem(0X%x, 0X%x) -> 0X%x", aValue, bValue,
426                     expr.longExpr(aValue, new Expr.MemL(bValue)));
427             }
428         }
429 
430         public static void runBinaryMemRegLongTest(Expr expr,
431                                                    int iterations,
432                                                    Random rng) {
433             if (!(expr.isLongExprSupported()
434                   && expr.isBinaryArgumentSupported()
435                   && expr.isMemExprSupported())) {
436                 return;
437             }
438 
439             for (int i = 0; i < iterations; i++) {
440                 long aValue = rng.nextLong();
441                 long bValue = rng.nextLong();
442                 log("BinaryLongMemReg(0X%x, 0X%x) -> 0X%x", aValue, bValue,
443                     expr.longExpr(new Expr.MemL(aValue), bValue));
444             }
445         }
446 
447         public static void runBinaryMemMemLongTest(Expr expr,
448                                                    int iterations,
449                                                    Random rng) {
450             if (!(expr.isLongExprSupported()
451                   && expr.isBinaryArgumentSupported()
452                   && expr.isMemExprSupported())) {
453                 return;
454             }
455 
456             for (int i = 0; i < iterations; i++) {
457                 long aValue = rng.nextLong();
458                 long bValue = rng.nextLong();
459                 log("BinaryLongMemMem(0X%x, 0X%x) -> 0X%x", aValue, bValue,
460                     expr.longExpr(new Expr.MemL(aValue),
461                                   new Expr.MemL(bValue)));
462             }
463         }
464     }
465 }