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