1 /*
   2  * Copyright (c) 2018, Red Hat, Inc. 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 /*
  25  * common code to run and validate tests of code generation for
  26  * volatile ops on AArch64
  27  *
  28  * incoming args are <testclass> <testtype>
  29  *
  30  * where <testclass> in {TestVolatileLoad,
  31  *                       TestVolatileStore,
  32  *                       TestUnsafeVolatileLoad,
  33  *                       TestUnsafeVolatileStore,
  34  *                       TestUnsafeVolatileCAS}
  35  * and <testtype> in {G1,
  36  *                    CMS,
  37  *                    CMSCondMark,
  38  *                    Serial,
  39  *                    Parallel}
  40  */
  41 
  42 
  43 package compiler.c2.aarch64;
  44 
  45 import java.util.List;
  46 import java.util.Iterator;
  47 import java.io.*;
  48 
  49 import jdk.test.lib.Asserts;
  50 import jdk.test.lib.compiler.InMemoryJavaCompiler;
  51 import jdk.test.lib.process.OutputAnalyzer;
  52 import jdk.test.lib.process.ProcessTools;
  53 
  54 // runner class that spawns a new JVM to exercises a combination of
  55 // volatile MemOp and GC. The ops are compiled with the dmb -->
  56 // ldar/stlr transforms either enabled or disabled. this runner parses
  57 // the PrintOptoAssembly output checking that the generated code is
  58 // correct.
  59 
  60 public class TestVolatiles {
  61     public void runtest(String classname, String testType) throws Throwable {
  62         // n.b. clients omit the package name for the class
  63         String fullclassname = "compiler.c2.aarch64." + classname;
  64         // build up a command line for the spawned JVM
  65         String[] procArgs;
  66         int argcount;
  67         // add one or two extra arguments according to test type
  68         // i.e. GC type plus GC conifg
  69         switch(testType) {
  70         case "G1":
  71             argcount = 8;
  72             procArgs = new String[argcount];
  73             procArgs[argcount - 2] = "-XX:+UseG1GC";
  74             break;
  75         case "Parallel":
  76             argcount = 8;
  77             procArgs = new String[argcount];
  78             procArgs[argcount - 2] = "-XX:+UseParallelGC";
  79             break;
  80         case "Serial":
  81             argcount = 8;
  82             procArgs = new String[argcount];
  83             procArgs[argcount - 2] = "-XX:+UseSerialGC";
  84             break;
  85         case "CMS":
  86             argcount = 9 ;
  87             procArgs = new String[argcount];
  88             procArgs[argcount - 3] = "-XX:+UseConcMarkSweepGC";
  89             procArgs[argcount - 2] = "-XX:-UseCondCardMark";
  90             break;
  91         case "CMSCondMark":
  92             argcount = 9 ;
  93             procArgs = new String[argcount];
  94             procArgs[argcount - 3] = "-XX:+UseConcMarkSweepGC";
  95             procArgs[argcount - 2] = "-XX:+UseCondCardMark";
  96             break;
  97         default:
  98             throw new RuntimeException("unexpected test type " + testType);
  99         }
 100 
 101         // fill in arguments common to all cases
 102 
 103         // the first round of test enables transform of barriers to
 104         // use acquiring loads and releasing stores by setting arg
 105         // zero appropriately. this arg is reset in the second run to
 106         // disable the transform.
 107 
 108         procArgs[0] = "-XX:-UseBarriersForVolatile";
 109 
 110         procArgs[1] = "-XX:-TieredCompilation";
 111         procArgs[2] = "-XX:+PrintOptoAssembly";
 112         procArgs[3] = "-XX:CompileCommand=compileonly," + fullclassname + "::" + "test*";
 113         procArgs[4] = "--add-exports";
 114         procArgs[5] = "java.base/jdk.internal.misc=ALL-UNNAMED";
 115         procArgs[argcount - 1] = fullclassname;
 116 
 117         ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(procArgs);
 118         OutputAnalyzer output = new OutputAnalyzer(pb.start());
 119 
 120         output.stderrShouldBeEmptyIgnoreVMWarnings();
 121         output.stdoutShouldNotBeEmpty();
 122         output.shouldHaveExitValue(0);
 123 
 124         // check the output for the correct asm sequence as
 125         // appropriate to test class, test type and whether transform
 126         // was applied
 127 
 128         checkoutput(output, classname, testType, false);
 129 
 130         // rerun the test class without the transform applied and
 131         // check the alternative generation is as expected
 132 
 133         procArgs[0] = "-XX:+UseBarriersForVolatile";
 134 
 135         pb = ProcessTools.createJavaProcessBuilder(procArgs);
 136         output = new OutputAnalyzer(pb.start());
 137 
 138         output.stderrShouldBeEmptyIgnoreVMWarnings();
 139         output.stdoutShouldNotBeEmpty();
 140         output.shouldHaveExitValue(0);
 141 
 142         // again check the output for the correct asm sequence
 143 
 144         checkoutput(output, classname, testType, true);
 145     }
 146 
 147     // skip through output returning a line containing the desireed
 148     // substring or null
 149     private String skipTo(Iterator<String> iter, String substring)
 150     {
 151         while (iter.hasNext()) {
 152             String nextLine = iter.next();
 153             if (nextLine.contains(substring)) {
 154                 return nextLine;
 155             }
 156         }
 157         return null;
 158     }
 159 
 160     // locate the start of compiler output for the desired method and
 161     // then check that each expected instruction occurs in the output
 162     // in the order supplied. throw an excpetion if not found.
 163     // n.b. the spawned JVM's output is included in the exception
 164     // message to make it easeir to identify what is missing.
 165 
 166     private void checkCompile(Iterator<String> iter, String methodname, String[] expected, OutputAnalyzer output)
 167     {
 168         // trace call to allow eyeball check of what we are checking against
 169         System.out.println("checkCompile(" + methodname + ",");
 170         String sepr = "  { ";
 171         for (String s : expected) {
 172             System.out.print(sepr);
 173             System.out.print(s);
 174             sepr = ",\n    ";
 175         }
 176         System.out.println(" })");
 177 
 178         // look for the start of an opto assembly print block
 179         String match = skipTo(iter, "{method}");
 180         if (match == null) {
 181             throw new RuntimeException("Missing compiler output for " + methodname + "!\n\n" + output.getOutput());
 182         }
 183         // check the compiled method name is right
 184         match = skipTo(iter, "- name:");
 185         if (match == null) {
 186             throw new RuntimeException("Missing compiled method name!\n\n" + output.getOutput());
 187         }
 188         if (!match.contains(methodname)) {
 189             throw new RuntimeException("Wrong method " + match + "!\n  -- expecting " + methodname + "\n\n" + output.getOutput());
 190         }
 191         // make sure we can match each expected term in order
 192         for (String s : expected) {
 193             match = skipTo(iter, s);
 194             if (match == null) {
 195                 throw new RuntimeException("Missing expected output " + s + "!\n\n" + output.getOutput());
 196             }
 197         }
 198     }
 199 
 200     // check for expected asm output from a volatile load
 201 
 202     private void checkload(OutputAnalyzer output, String testType, boolean useBarriersForVolatile) throws Throwable
 203     {
 204         Iterator<String> iter = output.asLines().listIterator();
 205 
 206         // we shoud see this same sequence for normal or unsafe volatile load
 207         // for both int and Object fields
 208 
 209         String[] matches;
 210 
 211         if (!useBarriersForVolatile) {
 212             matches = new String[] {
 213                 "ldarw",
 214                 "membar_acquire (elided)",
 215                 "ret"
 216             };
 217         } else {
 218             matches = new String[] {
 219                 "ldrw",
 220                 "membar_acquire",
 221                 "dmb ish",
 222                 "ret"
 223             };
 224         }
 225 
 226         checkCompile(iter, "testInt", matches, output);
 227 
 228         checkCompile(iter, "testObj", matches, output) ;
 229 
 230     }
 231 
 232     // check for expected asm output from a volatile store
 233 
 234     private void checkstore(OutputAnalyzer output, String testType, boolean useBarriersForVolatile) throws Throwable
 235     {
 236         Iterator<String> iter = output.asLines().listIterator();
 237 
 238         String[] matches;
 239 
 240         // non object stores are straightforward
 241         if (!useBarriersForVolatile) {
 242             // this is the sequence of instructions for all cases
 243             matches = new String[] {
 244                 "membar_release (elided)",
 245                 "stlrw",
 246                 "membar_volatile (elided)",
 247                 "ret"
 248             };
 249         } else {
 250             // this is the alternative sequence of instructions
 251             matches = new String[] {
 252                 "membar_release",
 253                 "dmb ish",
 254                 "strw",
 255                 "membar_volatile",
 256                 "dmb ish",
 257                 "ret"
 258             };
 259         }
 260 
 261         checkCompile(iter, "testInt", matches, output);
 262 
 263         // object stores will be as above except for when the GC
 264         // introduces barriers for card marking
 265 
 266         if (!useBarriersForVolatile) {
 267             switch (testType) {
 268             default:
 269                 // this is the basic sequence of instructions
 270                 matches = new String[] {
 271                     "membar_release (elided)",
 272                     "stlrw",
 273                     "membar_volatile (elided)",
 274                     "ret"
 275                 };
 276                 break;
 277             case "G1":
 278                 // a card mark volatile barrier should be generated
 279                 // before the card mark strb
 280                 matches = new String[] {
 281                     "membar_release (elided)",
 282                     "stlrw",
 283                     "membar_volatile",
 284                     "dmb ish",
 285                     "strb",
 286                     "membar_volatile (elided)",
 287                     "ret"
 288                 };
 289                 break;
 290             case "CMSCondMark":
 291                 // a card mark volatile barrier should be generated
 292                 // before the card mark strb from the StoreCM and the
 293                 // storestore barrier from the StoreCM should be elided
 294                 matches = new String[] {
 295                     "membar_release (elided)",
 296                     "stlrw",
 297                     "membar_volatile",
 298                     "dmb ish",
 299                     "storestore (elided)",
 300                     "strb",
 301                     "membar_volatile (elided)",
 302                     "ret"
 303                 };
 304                 break;
 305             case "CMS":
 306                 // a volatile card mark membar should not be generated
 307                 // before the card mark strb from the StoreCM and the
 308                 // storestore barrier from the StoreCM should be
 309                 // generated as "dmb ishst"
 310                 matches = new String[] {
 311                     "membar_release (elided)",
 312                     "stlrw",
 313                     "storestore",
 314                     "dmb ishst",
 315                     "strb",
 316                     "membar_volatile (elided)",
 317                     "ret"
 318                 };
 319                 break;
 320             }
 321         } else {
 322             switch (testType) {
 323             default:
 324                 // this is the basic sequence of instructions
 325                 matches = new String[] {
 326                     "membar_release",
 327                     "dmb ish",
 328                     "strw",
 329                     "membar_volatile",
 330                     "dmb ish",
 331                     "ret"
 332                 };
 333                 break;
 334             case "G1":
 335                 // a card mark volatile barrier should be generated
 336                 // before the card mark strb
 337                 matches = new String[] {
 338                     "membar_release",
 339                     "dmb ish",
 340                     "strw",
 341                     "membar_volatile",
 342                     "dmb ish",
 343                     "strb",
 344                     "membar_volatile",
 345                     "dmb ish",
 346                     "ret"
 347                 };
 348                 break;
 349             case "CMSCondMark":
 350                 // a card mark volatile barrier should be generated
 351                 // before the card mark strb from the StoreCM and the
 352                 // storestore barrier from the StoreCM should be elided
 353                 matches = new String[] {
 354                     "membar_release",
 355                     "dmb ish",
 356                     "strw",
 357                     "membar_volatile",
 358                     "dmb ish",
 359                     "storestore (elided)",
 360                     "strb",
 361                     "membar_volatile",
 362                     "dmb ish",
 363                     "ret"
 364                 };
 365                 break;
 366             case "CMS":
 367                 // a volatile card mark membar should not be generated
 368                 // before the card mark strb from the StoreCM and the
 369                 // storestore barrier from the StoreCM should be generated
 370                 // as "dmb ishst"
 371                 matches = new String[] {
 372                     "membar_release",
 373                     "dmb ish",
 374                     "strw",
 375                     "storestore",
 376                     "dmb ishst",
 377                     "strb",
 378                     "membar_volatile",
 379                     "dmb ish",
 380                     "ret"
 381                 };
 382                 break;
 383             }
 384         }
 385 
 386         checkCompile(iter, "testObj", matches, output);
 387     }
 388 
 389     // check for expected asm output from a volatile cas
 390 
 391     private void checkcas(OutputAnalyzer output, String testType, boolean useBarriersForVolatile) throws Throwable
 392     {
 393         Iterator<String> iter = output.asLines().listIterator();
 394 
 395         String[] matches;
 396 
 397         // non object stores are straightforward
 398         if (!useBarriersForVolatile) {
 399             // this is the sequence of instructions for all cases
 400             matches = new String[] {
 401                 "membar_release (elided)",
 402                 "cmpxchgw_acq",
 403                 "membar_acquire (elided)",
 404                 "ret"
 405             };
 406         } else {
 407             // this is the alternative sequence of instructions
 408             matches = new String[] {
 409                 "membar_release",
 410                 "dmb ish",
 411                 "cmpxchgw",
 412                 "membar_acquire",
 413                 "dmb ish",
 414                 "ret"
 415             };
 416         }
 417 
 418         checkCompile(iter, "testInt", matches, output);
 419 
 420         // object stores will be as above except for when the GC
 421         // introduces barriers for card marking
 422 
 423         if (!useBarriersForVolatile) {
 424             switch (testType) {
 425             default:
 426                 // this is the basic sequence of instructions
 427                 matches = new String[] {
 428                     "membar_release (elided)",
 429                     "cmpxchgw_acq",
 430                     "strb",
 431                     "membar_acquire (elided)",
 432                     "ret"
 433                 };
 434                 break;
 435             case "G1":
 436                 // a card mark volatile barrier should be generated
 437                 // before the card mark strb
 438                 matches = new String[] {
 439                     "membar_release (elided)",
 440                     "cmpxchgw_acq",
 441                     "membar_volatile",
 442                     "dmb ish",
 443                     "strb",
 444                     "membar_acquire (elided)",
 445                     "ret"
 446                 };
 447                 break;
 448             case "CMSCondMark":
 449                 // a card mark volatile barrier should be generated
 450                 // before the card mark strb from the StoreCM and the
 451                 // storestore barrier from the StoreCM should be elided
 452                 matches = new String[] {
 453                     "membar_release (elided)",
 454                     "cmpxchgw_acq",
 455                     "membar_volatile",
 456                     "dmb ish",
 457                     "storestore (elided)",
 458                     "strb",
 459                     "membar_acquire (elided)",
 460                     "ret"
 461                 };
 462                 break;
 463             case "CMS":
 464                 // a volatile card mark membar should not be generated
 465                 // before the card mark strb from the StoreCM and the
 466                 // storestore barrier from the StoreCM should be elided
 467                 matches = new String[] {
 468                     "membar_release (elided)",
 469                     "cmpxchgw_acq",
 470                     "storestore",
 471                     "dmb ishst",
 472                     "strb",
 473                     "membar_acquire (elided)",
 474                     "ret"
 475                 };
 476                 break;
 477             }
 478         } else {
 479             switch (testType) {
 480             default:
 481                 // this is the basic sequence of instructions
 482                 matches = new String[] {
 483                     "membar_release",
 484                     "dmb ish",
 485                     "cmpxchgw",
 486                     "membar_acquire",
 487                     "dmb ish",
 488                     "ret"
 489                 };
 490                 break;
 491             case "G1":
 492                 // a card mark volatile barrier should be generated
 493                 // before the card mark strb
 494                 matches = new String[] {
 495                     "membar_release",
 496                     "dmb ish",
 497                     "cmpxchgw",
 498                     "membar_volatile",
 499                     "dmb ish",
 500                     "strb",
 501                     "membar_acquire",
 502                     "dmb ish",
 503                     "ret"
 504                 };
 505                 break;
 506             case "CMSCondMark":
 507                 // a card mark volatile barrier should be generated
 508                 // before the card mark strb from the StoreCM and the
 509                 // storestore barrier from the StoreCM should be elided
 510                 matches = new String[] {
 511                     "membar_release",
 512                     "dmb ish",
 513                     "cmpxchgw",
 514                     "membar_volatile",
 515                     "dmb ish",
 516                     "storestore (elided)",
 517                     "strb",
 518                     "membar_acquire",
 519                     "dmb ish",
 520                     "ret"
 521                 };
 522                 break;
 523             case "CMS":
 524                 // a volatile card mark membar should not be generated
 525                 // before the card mark strb from the StoreCM and the
 526                 // storestore barrier from the StoreCM should be generated
 527                 // as "dmb ishst"
 528                 matches = new String[] {
 529                     "membar_release",
 530                     "dmb ish",
 531                     "cmpxchgw",
 532                     "storestore",
 533                     "dmb ishst",
 534                     "strb",
 535                     "membar_acquire",
 536                     "dmb ish",
 537                     "ret"
 538                 };
 539                 break;
 540             }
 541         }
 542 
 543         checkCompile(iter, "testObj", matches, output);
 544     }
 545 
 546     // perform a check appropriate to the classname
 547 
 548     private void checkoutput(OutputAnalyzer output, String classname, String testType, boolean useBarriersForVolatile) throws Throwable
 549     {
 550         // trace call to allow eyeball check of what is being checked
 551         System.out.println("checkoutput(" +
 552                            classname + ", " +
 553                            testType + ", " +
 554                            useBarriersForVolatile + ")\n" +
 555                            output.getOutput());
 556 
 557         switch (classname) {
 558         case "TestVolatileLoad":
 559             checkload(output, testType, useBarriersForVolatile);
 560             break;
 561         case "TestVolatileStore":
 562             checkstore(output, testType, useBarriersForVolatile);
 563             break;
 564         case "TestUnsafeVolatileLoad":
 565             checkload(output, testType, useBarriersForVolatile);
 566             break;
 567         case "TestUnsafeVolatileStore":
 568             checkstore(output, testType, useBarriersForVolatile);
 569             break;
 570         case "TestUnsafeVolatileCAS":
 571             checkcas(output, testType, useBarriersForVolatile);
 572             break;
 573         }
 574     }
 575 }