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