1 /*
   2  * Copyright (c) 2011, 2018, 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 
  25 package org.graalvm.compiler.core.test;
  26 
  27 import static org.graalvm.compiler.core.common.GraalOptions.OptImplicitNullChecks;
  28 import static org.graalvm.compiler.core.common.GraalOptions.OptScheduleOutOfLoops;
  29 import static org.graalvm.compiler.graph.test.matchers.NodeIterableCount.hasCount;
  30 import static org.hamcrest.core.IsInstanceOf.instanceOf;
  31 import static org.junit.Assert.assertThat;
  32 
  33 import java.util.ArrayList;
  34 import java.util.List;
  35 
  36 import org.graalvm.compiler.api.directives.GraalDirectives;
  37 import org.graalvm.compiler.debug.DebugContext;
  38 import org.graalvm.compiler.graph.Node;
  39 import org.graalvm.compiler.graph.iterators.NodeIterable;
  40 import org.graalvm.compiler.nodes.ReturnNode;
  41 import org.graalvm.compiler.nodes.StartNode;
  42 import org.graalvm.compiler.nodes.StructuredGraph;
  43 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
  44 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
  45 import org.graalvm.compiler.nodes.cfg.Block;
  46 import org.graalvm.compiler.nodes.memory.FloatingReadNode;
  47 import org.graalvm.compiler.nodes.memory.WriteNode;
  48 import org.graalvm.compiler.nodes.spi.LoweringTool;
  49 import org.graalvm.compiler.options.OptionValues;
  50 import org.graalvm.compiler.phases.OptimisticOptimizations;
  51 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
  52 import org.graalvm.compiler.phases.common.FloatingReadPhase;
  53 import org.graalvm.compiler.phases.common.GuardLoweringPhase;
  54 import org.graalvm.compiler.phases.common.LoweringPhase;
  55 import org.graalvm.compiler.phases.common.RemoveValueProxyPhase;
  56 import org.graalvm.compiler.phases.schedule.SchedulePhase;
  57 import org.graalvm.compiler.phases.schedule.SchedulePhase.SchedulingStrategy;
  58 import org.graalvm.compiler.phases.tiers.HighTierContext;
  59 import org.graalvm.compiler.phases.tiers.MidTierContext;
  60 import org.junit.Assert;
  61 import org.junit.Test;
  62 
  63 /**
  64  * In these test the FrameStates are explicitly cleared out, so that the scheduling of
  65  * FloatingReadNodes depends solely on the scheduling algorithm. The FrameStates normally keep the
  66  * FloatingReadNodes above a certain point, so that they (most of the time...) magically do the
  67  * right thing.
  68  *
  69  * The scheduling shouldn't depend on FrameStates, which is tested by this class.
  70  */
  71 public class MemoryScheduleTest extends GraphScheduleTest {
  72 
  73     private enum TestMode {
  74         WITH_FRAMESTATES,
  75         WITHOUT_FRAMESTATES,
  76         INLINED_WITHOUT_FRAMESTATES
  77     }
  78 
  79     public static class Container {
  80 
  81         public int a;
  82         public int b;
  83         public int c;
  84 
  85         public Object obj;
  86     }
  87 
  88     private static final Container container = new Container();
  89     private static final List<Container> containerList = new ArrayList<>();
  90     private static final double LOOP_ENTRY_PROBABILITY = 0.9;
  91 
  92     /**
  93      * In this test the read should be scheduled before the write.
  94      */
  95     public static int testSimpleSnippet() {
  96         try {
  97             return container.a;
  98         } finally {
  99             container.a = 15;
 100         }
 101     }
 102 
 103     @Test
 104     public void testSimple() {
 105         for (TestMode mode : TestMode.values()) {
 106             ScheduleResult schedule = getFinalSchedule("testSimpleSnippet", mode);
 107             StructuredGraph graph = schedule.getCFG().graph;
 108             assertReadAndWriteInSameBlock(schedule, true);
 109             assertOrderedAfterSchedule(schedule, graph.getNodes().filter(FloatingReadNode.class).first(), graph.getNodes().filter(WriteNode.class).first());
 110         }
 111     }
 112 
 113     /**
 114      * In this case the read should be scheduled in the first block.
 115      */
 116     public static int testSplit1Snippet(int a) {
 117         try {
 118             return container.a;
 119         } finally {
 120             if (a < 0) {
 121                 container.a = 15;
 122             } else {
 123                 container.b = 15;
 124             }
 125         }
 126     }
 127 
 128     @Test
 129     public void testSplit1() {
 130         for (TestMode mode : TestMode.values()) {
 131             ScheduleResult schedule = getFinalSchedule("testSplit1Snippet", mode);
 132             assertReadWithinStartBlock(schedule, true);
 133             assertReadWithinAllReturnBlocks(schedule, false);
 134         }
 135     }
 136 
 137     /**
 138      * Here the read should float to the end.
 139      */
 140     public static int testSplit2Snippet(int a) {
 141         try {
 142             return container.a;
 143         } finally {
 144             if (a < 0) {
 145                 container.c = 15;
 146             } else {
 147                 container.b = 15;
 148             }
 149             container.obj = null;
 150         }
 151     }
 152 
 153     @Test
 154     public void testSplit2() {
 155         ScheduleResult schedule = getFinalSchedule("testSplit2Snippet", TestMode.WITHOUT_FRAMESTATES);
 156         assertReadWithinStartBlock(schedule, false);
 157         assertReadWithinAllReturnBlocks(schedule, true);
 158     }
 159 
 160     /**
 161      * Here the read should not float to the end.
 162      */
 163     public static int testLoop1Snippet(int a, int b) {
 164         try {
 165             return container.a;
 166         } finally {
 167             for (int i = 0; GraalDirectives.injectBranchProbability(LOOP_ENTRY_PROBABILITY, i < a); i++) {
 168                 if (b < 0) {
 169                     container.b = 10;
 170                 } else {
 171                     container.a = 15;
 172                 }
 173             }
 174         }
 175     }
 176 
 177     @Test
 178     public void testLoop1() {
 179         ScheduleResult schedule = getFinalSchedule("testLoop1Snippet", TestMode.WITHOUT_FRAMESTATES);
 180         assertDeepEquals(6, schedule.getCFG().getBlocks().length);
 181         assertReadWithinStartBlock(schedule, true);
 182         assertReadWithinAllReturnBlocks(schedule, false);
 183     }
 184 
 185     /**
 186      * Here the read should float to the end.
 187      */
 188     public static int testLoop2Snippet(int a, int b) {
 189         try {
 190             return container.a;
 191         } finally {
 192             for (int i = 0; GraalDirectives.injectBranchProbability(LOOP_ENTRY_PROBABILITY, i < a); i++) {
 193                 if (b < 0) {
 194                     container.b = 10;
 195                 } else {
 196                     container.c = 15;
 197                 }
 198             }
 199         }
 200     }
 201 
 202     @Test
 203     public void testLoop2() {
 204         ScheduleResult schedule = getFinalSchedule("testLoop2Snippet", TestMode.WITHOUT_FRAMESTATES);
 205         assertDeepEquals(6, schedule.getCFG().getBlocks().length);
 206         assertReadWithinStartBlock(schedule, false);
 207         assertReadWithinAllReturnBlocks(schedule, true);
 208     }
 209 
 210     /**
 211      * Here the read should float out of the loop.
 212      */
 213     public static int testLoop3Snippet(int a) {
 214         int j = 0;
 215         for (int i = 0; GraalDirectives.injectBranchProbability(LOOP_ENTRY_PROBABILITY, i < a); i++) {
 216             if (i - container.a == 0) {
 217                 break;
 218             }
 219             j++;
 220         }
 221         return j;
 222     }
 223 
 224     @Test
 225     public void testLoop3() {
 226         ScheduleResult schedule = getFinalSchedule("testLoop3Snippet", TestMode.WITHOUT_FRAMESTATES);
 227         assertDeepEquals(6, schedule.getCFG().getBlocks().length);
 228         assertReadWithinStartBlock(schedule, true);
 229         assertReadWithinAllReturnBlocks(schedule, false);
 230     }
 231 
 232     public String testStringReplaceSnippet(String input) {
 233         return input.replace('a', 'b');
 234     }
 235 
 236     @Test
 237     public void testStringReplace() {
 238         getFinalSchedule("testStringReplaceSnippet", TestMode.INLINED_WITHOUT_FRAMESTATES);
 239         test("testStringReplaceSnippet", "acbaaa");
 240     }
 241 
 242     /**
 243      * Here the read should float out of the loop.
 244      */
 245     public static int testLoop5Snippet(int a, int b, MemoryScheduleTest obj) {
 246         int ret = 0;
 247         int bb = b;
 248         for (int i = 0; GraalDirectives.injectBranchProbability(LOOP_ENTRY_PROBABILITY, i < a); i++) {
 249             ret = obj.hash;
 250             if (a > 10) {
 251                 bb++;
 252             } else {
 253                 bb--;
 254             }
 255             ret = ret / 10;
 256         }
 257         return ret + bb;
 258     }
 259 
 260     @Test
 261     public void testLoop5() {
 262         ScheduleResult schedule = getFinalSchedule("testLoop5Snippet", TestMode.WITHOUT_FRAMESTATES);
 263         assertDeepEquals(10, schedule.getCFG().getBlocks().length);
 264         assertReadWithinStartBlock(schedule, false);
 265         assertReadWithinAllReturnBlocks(schedule, false);
 266     }
 267 
 268     /**
 269      * Here the read should not float out of the loop.
 270      */
 271     public static int testLoop6Snippet(int a, int b, MemoryScheduleTest obj) {
 272         int ret = 0;
 273         int bb = b;
 274         for (int i = 0; GraalDirectives.injectBranchProbability(LOOP_ENTRY_PROBABILITY, i < a); i++) {
 275             ret = obj.hash;
 276             if (a > 10) {
 277                 bb++;
 278             } else {
 279                 bb--;
 280                 for (int j = 0; GraalDirectives.injectBranchProbability(LOOP_ENTRY_PROBABILITY, j < b); ++j) {
 281                     obj.hash = 3;
 282                 }
 283             }
 284             ret = ret / 10;
 285         }
 286         return ret + bb;
 287     }
 288 
 289     @Test
 290     public void testLoop6() {
 291         ScheduleResult schedule = getFinalSchedule("testLoop6Snippet", TestMode.WITHOUT_FRAMESTATES);
 292         assertDeepEquals(13, schedule.getCFG().getBlocks().length);
 293         assertReadWithinStartBlock(schedule, false);
 294         assertReadWithinAllReturnBlocks(schedule, false);
 295     }
 296 
 297     /**
 298      * Here the read should not float out of the loop.
 299      */
 300     public static int testLoop7Snippet(int a, int b, MemoryScheduleTest obj) {
 301         int ret = 0;
 302         int bb = b;
 303         for (int i = 0; GraalDirectives.injectBranchProbability(LOOP_ENTRY_PROBABILITY, i < a); i++) {
 304             ret = obj.hash;
 305             if (a > 10) {
 306                 bb++;
 307             } else {
 308                 bb--;
 309                 for (int k = 0; GraalDirectives.injectBranchProbability(LOOP_ENTRY_PROBABILITY, k < a); ++k) {
 310                     if (k % 2 == 1) {
 311                         for (int j = 0; GraalDirectives.injectBranchProbability(LOOP_ENTRY_PROBABILITY, j < b); ++j) {
 312                             obj.hash = 3;
 313                         }
 314                     }
 315                 }
 316             }
 317             ret = ret / 10;
 318         }
 319         return ret + bb;
 320     }
 321 
 322     @Test
 323     public void testLoop7() {
 324         ScheduleResult schedule = getFinalSchedule("testLoop7Snippet", TestMode.WITHOUT_FRAMESTATES);
 325         assertDeepEquals(18, schedule.getCFG().getBlocks().length);
 326         assertReadWithinStartBlock(schedule, false);
 327         assertReadWithinAllReturnBlocks(schedule, false);
 328     }
 329 
 330     /**
 331      * Here the read should not float to the end.
 332      */
 333     public static int testLoop8Snippet(int a, int b) {
 334         int result = container.a;
 335         for (int i = 0; GraalDirectives.injectBranchProbability(LOOP_ENTRY_PROBABILITY, i < a); i++) {
 336             if (b < 0) {
 337                 container.b = 10;
 338                 break;
 339             } else {
 340                 for (int j = 0; GraalDirectives.injectBranchProbability(LOOP_ENTRY_PROBABILITY, j < b); j++) {
 341                     container.a = 0;
 342                 }
 343             }
 344         }
 345         GraalDirectives.controlFlowAnchor();
 346         return result;
 347     }
 348 
 349     @Test
 350     public void testLoop8() {
 351         ScheduleResult schedule = getFinalSchedule("testLoop8Snippet", TestMode.WITHOUT_FRAMESTATES);
 352         assertDeepEquals(10, schedule.getCFG().getBlocks().length);
 353         assertReadWithinStartBlock(schedule, true);
 354         assertReadWithinAllReturnBlocks(schedule, false);
 355     }
 356 
 357     /**
 358      * Here the read should float after the loop.
 359      */
 360     public static int testLoop9Snippet(int a, int b) {
 361         container.a = b;
 362         for (int i = 0; i < a; i++) {
 363             container.a = i;
 364         }
 365         GraalDirectives.controlFlowAnchor();
 366         return container.a;
 367     }
 368 
 369     @Test
 370     public void testLoop9() {
 371         ScheduleResult schedule = getFinalSchedule("testLoop9Snippet", TestMode.WITHOUT_FRAMESTATES);
 372         StructuredGraph graph = schedule.getCFG().getStartBlock().getBeginNode().graph();
 373         assertThat(graph.getNodes(ReturnNode.TYPE), hasCount(1));
 374         ReturnNode ret = graph.getNodes(ReturnNode.TYPE).first();
 375         assertThat(ret.result(), instanceOf(FloatingReadNode.class));
 376         Block readBlock = schedule.getNodeToBlockMap().get(ret.result());
 377         Assert.assertEquals(0, readBlock.getLoopDepth());
 378     }
 379 
 380     /**
 381      * Here the read should not float to the end.
 382      */
 383     public static int testIfRead1Snippet(int a) {
 384         int res = container.a;
 385         if (a < 0) {
 386             container.a = 10;
 387         }
 388         return res;
 389     }
 390 
 391     @Test
 392     public void testIfRead1() {
 393         ScheduleResult schedule = getFinalSchedule("testIfRead1Snippet", TestMode.WITHOUT_FRAMESTATES);
 394         assertDeepEquals(3, schedule.getCFG().getBlocks().length);
 395         assertReadWithinStartBlock(schedule, true);
 396         assertReadAndWriteInSameBlock(schedule, false);
 397     }
 398 
 399     /**
 400      * Here the read should float in the else block.
 401      */
 402     public static int testIfRead2Snippet(int a) {
 403         int res = 0;
 404         if (a < 0) {
 405             container.a = 10;
 406         } else {
 407             res = container.a;
 408         }
 409         return res;
 410     }
 411 
 412     @Test
 413     public void testIfRead2() {
 414         ScheduleResult schedule = getFinalSchedule("testIfRead2Snippet", TestMode.WITHOUT_FRAMESTATES);
 415         assertDeepEquals(3, schedule.getCFG().getBlocks().length);
 416         assertDeepEquals(1, schedule.getCFG().graph.getNodes().filter(FloatingReadNode.class).count());
 417         assertReadWithinStartBlock(schedule, false);
 418         assertReadWithinAllReturnBlocks(schedule, false);
 419         assertReadAndWriteInSameBlock(schedule, false);
 420     }
 421 
 422     /**
 423      * Here the read should float to the end, right before the write.
 424      */
 425     public static int testIfRead3Snippet(int a) {
 426         if (a < 0) {
 427             container.a = 10;
 428         }
 429         int res = container.a;
 430         container.a = 20;
 431         return res;
 432     }
 433 
 434     @Test
 435     public void testIfRead3() {
 436         ScheduleResult schedule = getFinalSchedule("testIfRead3Snippet", TestMode.WITHOUT_FRAMESTATES);
 437         assertDeepEquals(4, schedule.getCFG().getBlocks().length);
 438         assertReadWithinStartBlock(schedule, false);
 439         assertReadWithinAllReturnBlocks(schedule, true);
 440     }
 441 
 442     /**
 443      * Here the read should be just in the if branch (with the write).
 444      */
 445     public static int testIfRead4Snippet(int a) {
 446         if (a > 0) {
 447             int res = container.a;
 448             container.a = 0x20;
 449             return res;
 450         } else {
 451             return 0x10;
 452         }
 453     }
 454 
 455     @Test
 456     public void testIfRead4() {
 457         ScheduleResult schedule = getFinalSchedule("testIfRead4Snippet", TestMode.WITHOUT_FRAMESTATES);
 458         assertDeepEquals(3, schedule.getCFG().getBlocks().length);
 459         assertReadWithinStartBlock(schedule, false);
 460         assertReadWithinAllReturnBlocks(schedule, false);
 461         assertReadAndWriteInSameBlock(schedule, true);
 462     }
 463 
 464     /**
 465      * Here the read should float to the end.
 466      */
 467     public static int testIfRead5Snippet(int a) {
 468         if (a < 0) {
 469             container.a = 10;
 470         }
 471         return container.a;
 472     }
 473 
 474     @Test
 475     public void testIfRead5() {
 476         ScheduleResult schedule = getFinalSchedule("testIfRead5Snippet", TestMode.WITHOUT_FRAMESTATES);
 477         assertDeepEquals(4, schedule.getCFG().getBlocks().length);
 478         assertReadWithinStartBlock(schedule, false);
 479         assertReadWithinAllReturnBlocks(schedule, true);
 480         assertReadAndWriteInSameBlock(schedule, false);
 481     }
 482 
 483     public static int testAntiDependencySnippet(int a) {
 484         /*
 485          * This read must not be scheduled after the following write.
 486          */
 487         int res = container.a;
 488         container.a = 10;
 489 
 490         /*
 491          * Add some more basic blocks.
 492          */
 493         if (a < 0) {
 494             container.b = 20;
 495         }
 496         container.c = 30;
 497         return res;
 498     }
 499 
 500     @Test
 501     public void testAntiDependency() {
 502         ScheduleResult schedule = getFinalSchedule("testAntiDependencySnippet", TestMode.WITHOUT_FRAMESTATES);
 503         assertDeepEquals(4, schedule.getCFG().getBlocks().length);
 504         assertReadBeforeAllWritesInStartBlock(schedule);
 505     }
 506 
 507     /**
 508      * testing scheduling within a block.
 509      */
 510     public static int testBlockScheduleSnippet() {
 511         int res = 0;
 512         container.a = 0x00;
 513         container.a = 0x10;
 514         container.a = 0x20;
 515         container.a = 0x30;
 516         container.a = 0x40;
 517         res = container.a;
 518         container.a = 0x50;
 519         container.a = 0x60;
 520         container.a = 0x70;
 521         return res;
 522     }
 523 
 524     @Test
 525     public void testBlockSchedule() {
 526         ScheduleResult schedule = getFinalSchedule("testBlockScheduleSnippet", TestMode.WITHOUT_FRAMESTATES);
 527         StructuredGraph graph = schedule.getCFG().graph;
 528         NodeIterable<WriteNode> writeNodes = graph.getNodes().filter(WriteNode.class);
 529 
 530         assertDeepEquals(1, schedule.getCFG().getBlocks().length);
 531         assertDeepEquals(8, writeNodes.count());
 532         assertDeepEquals(1, graph.getNodes().filter(FloatingReadNode.class).count());
 533 
 534         FloatingReadNode read = graph.getNodes().filter(FloatingReadNode.class).first();
 535 
 536         WriteNode[] writes = new WriteNode[8];
 537         int i = 0;
 538         for (WriteNode n : writeNodes) {
 539             writes[i] = n;
 540             i++;
 541         }
 542         assertOrderedAfterSchedule(schedule, writes[4], read);
 543         assertOrderedAfterSchedule(schedule, read, writes[5]);
 544         for (int j = 0; j < 7; j++) {
 545             assertOrderedAfterSchedule(schedule, writes[j], writes[j + 1]);
 546         }
 547     }
 548 
 549     /**
 550      * read should move inside the loop (out of loop is disabled).
 551      */
 552     public static int testBlockSchedule2Snippet(int value) {
 553         int res = 0;
 554 
 555         container.a = value;
 556         for (int i = 0; i < 100; i++) {
 557             if (i == 10) {
 558                 return container.a;
 559             }
 560             res += i;
 561         }
 562         return res;
 563     }
 564 
 565     @Test
 566     public void testBlockSchedule2() {
 567         ScheduleResult schedule = getFinalSchedule("testBlockSchedule2Snippet", TestMode.WITHOUT_FRAMESTATES, SchedulingStrategy.LATEST);
 568         assertReadWithinStartBlock(schedule, false);
 569         assertReadWithinAllReturnBlocks(schedule, false);
 570         assertReadAndWriteInSameBlock(schedule, false);
 571     }
 572 
 573     public static void testProxySnippet() {
 574         while (container.a < container.b) {
 575             List<Container> list = new ArrayList<>(containerList);
 576             while (container.c < list.size()) {
 577                 if (container.obj != null) {
 578                     return;
 579                 }
 580                 container.c++;
 581             }
 582             container.a = 0;
 583             container.b--;
 584         }
 585         container.b++;
 586     }
 587 
 588     @Test
 589     public void testProxy() {
 590         ScheduleResult schedule = getFinalSchedule("testProxySnippet", TestMode.WITHOUT_FRAMESTATES);
 591         assertReadWithinStartBlock(schedule, false);
 592         assertReadWithinAllReturnBlocks(schedule, false);
 593     }
 594 
 595     private int hash = 0;
 596     private final char[] value = new char[3];
 597 
 598     public int testStringHashCodeSnippet() {
 599         int h = hash;
 600         if (h == 0 && value.length > 0) {
 601             char[] val = value;
 602 
 603             for (int i = 0; i < value.length; i++) {
 604                 h = 31 * h + val[i];
 605             }
 606             hash = h;
 607         }
 608         return h;
 609     }
 610 
 611     @Test
 612     public void testStringHashCode() {
 613         ScheduleResult schedule = getFinalSchedule("testStringHashCodeSnippet", TestMode.WITHOUT_FRAMESTATES);
 614         assertReadWithinStartBlock(schedule, true);
 615         assertReadWithinAllReturnBlocks(schedule, false);
 616 
 617         hash = 0x1337;
 618         value[0] = 'a';
 619         value[1] = 'b';
 620         value[2] = 'c';
 621         test("testStringHashCodeSnippet");
 622     }
 623 
 624     public static int testLoop4Snippet(int count) {
 625         int[] a = new int[count];
 626 
 627         for (int i = 0; GraalDirectives.injectBranchProbability(LOOP_ENTRY_PROBABILITY, i < a.length); i++) {
 628             a[i] = i;
 629         }
 630 
 631         int i = 0;
 632         int iwrap = count - 1;
 633         int sum = 0;
 634 
 635         while (GraalDirectives.injectBranchProbability(LOOP_ENTRY_PROBABILITY, i < count)) {
 636             sum += (a[i] + a[iwrap]) / 2;
 637             iwrap = i;
 638             i++;
 639         }
 640         return sum;
 641     }
 642 
 643     @Test
 644     public void testLoop4() {
 645         ScheduleResult schedule = getFinalSchedule("testLoop4Snippet", TestMode.WITHOUT_FRAMESTATES);
 646         assertReadWithinStartBlock(schedule, false);
 647         assertReadWithinAllReturnBlocks(schedule, false);
 648     }
 649 
 650     private void assertReadWithinAllReturnBlocks(ScheduleResult schedule, boolean withinReturnBlock) {
 651         StructuredGraph graph = schedule.getCFG().graph;
 652         assertTrue(graph.getNodes(ReturnNode.TYPE).isNotEmpty());
 653 
 654         int withRead = 0;
 655         int returnBlocks = 0;
 656         for (ReturnNode returnNode : graph.getNodes(ReturnNode.TYPE)) {
 657             Block block = schedule.getCFG().getNodeToBlock().get(returnNode);
 658             for (Node node : schedule.getBlockToNodesMap().get(block)) {
 659                 if (node instanceof FloatingReadNode) {
 660                     withRead++;
 661                     break;
 662                 }
 663             }
 664             returnBlocks++;
 665         }
 666         assertDeepEquals(withRead == returnBlocks, withinReturnBlock);
 667     }
 668 
 669     private void assertReadWithinStartBlock(ScheduleResult schedule, boolean withinStartBlock) {
 670         boolean readEncountered = false;
 671         for (Node node : schedule.getBlockToNodesMap().get(schedule.getCFG().getStartBlock())) {
 672             if (node instanceof FloatingReadNode) {
 673                 readEncountered = true;
 674             }
 675         }
 676         assertDeepEquals(withinStartBlock, readEncountered);
 677     }
 678 
 679     private static void assertReadAndWriteInSameBlock(ScheduleResult schedule, boolean inSame) {
 680         StructuredGraph graph = schedule.getCFG().graph;
 681         FloatingReadNode read = graph.getNodes().filter(FloatingReadNode.class).first();
 682         WriteNode write = graph.getNodes().filter(WriteNode.class).first();
 683         assertTrue(!(inSame ^ schedule.getCFG().blockFor(read) == schedule.getCFG().blockFor(write)));
 684     }
 685 
 686     private static void assertReadBeforeAllWritesInStartBlock(ScheduleResult schedule) {
 687         boolean writeNodeFound = false;
 688         boolean readNodeFound = false;
 689         for (Node node : schedule.nodesFor(schedule.getCFG().getStartBlock())) {
 690             if (node instanceof FloatingReadNode) {
 691                 assertTrue(!writeNodeFound);
 692                 readNodeFound = true;
 693             } else if (node instanceof WriteNode) {
 694                 writeNodeFound = true;
 695             }
 696         }
 697         assertTrue(readNodeFound);
 698     }
 699 
 700     private ScheduleResult getFinalSchedule(final String snippet, final TestMode mode) {
 701         return getFinalSchedule(snippet, mode, SchedulingStrategy.LATEST_OUT_OF_LOOPS);
 702     }
 703 
 704     @SuppressWarnings("try")
 705     private ScheduleResult getFinalSchedule(final String snippet, final TestMode mode, final SchedulingStrategy schedulingStrategy) {
 706         OptionValues options = new OptionValues(getInitialOptions(), OptScheduleOutOfLoops, schedulingStrategy == SchedulingStrategy.LATEST_OUT_OF_LOOPS, OptImplicitNullChecks, false);
 707         final StructuredGraph graph = parseEager(snippet, AllowAssumptions.NO, options);
 708         DebugContext debug = graph.getDebug();
 709         try (DebugContext.Scope d = debug.scope("FloatingReadTest", graph)) {
 710             HighTierContext context = getDefaultHighTierContext();
 711             CanonicalizerPhase canonicalizer = new CanonicalizerPhase();
 712             canonicalizer.apply(graph, context);
 713             if (mode == TestMode.INLINED_WITHOUT_FRAMESTATES) {
 714                 createInliningPhase(canonicalizer).apply(graph, context);
 715             }
 716             new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
 717             if (mode == TestMode.WITHOUT_FRAMESTATES || mode == TestMode.INLINED_WITHOUT_FRAMESTATES) {
 718                 graph.clearAllStateAfter();
 719             }
 720             debug.dump(DebugContext.BASIC_LEVEL, graph, "after removal of framestates");
 721 
 722             new FloatingReadPhase().apply(graph);
 723             new RemoveValueProxyPhase().apply(graph);
 724 
 725             MidTierContext midContext = new MidTierContext(getProviders(), getTargetProvider(), OptimisticOptimizations.ALL, graph.getProfilingInfo());
 726             new GuardLoweringPhase().apply(graph, midContext);
 727             new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, midContext);
 728             new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.LOW_TIER).apply(graph, midContext);
 729 
 730             SchedulePhase schedule = new SchedulePhase(schedulingStrategy);
 731             schedule.apply(graph);
 732             assertDeepEquals(1, graph.getNodes().filter(StartNode.class).count());
 733             return graph.getLastSchedule();
 734         } catch (Throwable e) {
 735             throw debug.handle(e);
 736         }
 737     }
 738 }