1 /* 2 * Copyright (c) 2016, 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 package org.graalvm.compiler.hotspot.test; 24 25 import java.lang.management.ManagementFactory; 26 import java.lang.management.MonitorInfo; 27 import java.lang.management.RuntimeMXBean; 28 import java.lang.management.ThreadInfo; 29 import java.lang.management.ThreadMXBean; 30 import java.util.Collection; 31 import java.util.HashMap; 32 import java.util.HashSet; 33 import java.util.List; 34 35 import org.graalvm.compiler.api.directives.GraalDirectives; 36 import org.graalvm.compiler.core.phases.HighTier; 37 import org.graalvm.compiler.debug.DebugContext; 38 import org.graalvm.compiler.debug.GraalError; 39 import org.graalvm.compiler.debug.TTY; 40 import org.graalvm.compiler.hotspot.phases.OnStackReplacementPhase; 41 import org.graalvm.compiler.options.OptionKey; 42 import org.graalvm.compiler.options.OptionValues; 43 import org.graalvm.util.EconomicMap; 44 import org.junit.Assert; 45 import org.junit.Test; 46 47 import jdk.vm.ci.meta.ResolvedJavaMethod; 48 import org.junit.Assume; 49 import org.junit.BeforeClass; 50 51 /** 52 * Test on-stack-replacement with locks. 53 */ 54 public class GraalOSRLockTest extends GraalOSRTestBase { 55 56 private static boolean TestInSeparateThread = false; 57 private static final String COMPILE_ONLY_FLAG = "-Xcomp"; 58 59 @BeforeClass 60 public static void checkVMArguments() { 61 try { 62 Class.forName("java.lang.management.ManagementFactory"); 63 } catch (ClassNotFoundException ex) { 64 Assume.assumeNoException("cannot check for monitors without java.management JDK9 module", ex); 65 } 66 /* 67 * Note: The -Xcomp execution mode of the VM will stop most of the OSR test cases from 68 * working as every method is compiled at level3 (followed by level4 on the second 69 * invocation). The tests in this class are written in a way that they expect a method to be 70 * executed at the invocation BCI with the interpreter and then perform an OSR to an 71 * installed nmethod at a given BCI. 72 * 73 */ 74 RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean(); 75 List<String> arguments = runtimeMxBean.getInputArguments(); 76 for (String arg : arguments) { 77 Assume.assumeFalse(arg.equals(COMPILE_ONLY_FLAG)); 78 } 79 } 80 81 // testing only 82 public static boolean isMonitorLockHeld(Object o) { 83 return isMonitorLockHeldByThread(o, null); 84 } 85 86 public static boolean isMonitorLockHeldByThread(Object o, Thread t) { 87 int oihc = System.identityHashCode(o); 88 ThreadMXBean tmxbean = ManagementFactory.getThreadMXBean(); 89 ThreadInfo[] tinfos = tmxbean.dumpAllThreads(true, false); 90 91 for (ThreadInfo ti : tinfos) { 92 if (!(t != null && t.getId() != ti.getThreadId())) { 93 for (MonitorInfo mi : ti.getLockedMonitors()) { 94 if (mi.getIdentityHashCode() == oihc) { 95 return true; 96 } 97 } 98 } 99 } 100 return false; 101 } 102 103 protected static void run(Runnable r) { 104 if (TestInSeparateThread) { 105 Thread t = new Thread(new Runnable() { 106 @Override 107 public void run() { 108 beforeOSRLockTest(); 109 r.run(); 110 afterOSRLockTest(); 111 } 112 }); 113 t.start(); 114 try { 115 t.join(); 116 } catch (Throwable t1) { 117 throw new GraalError(t1); 118 } 119 } else { 120 beforeOSRLockTest(); 121 r.run(); 122 afterOSRLockTest(); 123 } 124 } 125 126 private static boolean wasLocked() { 127 return isMonitorLockHeld(lock) || isMonitorLockHeld(lock1); 128 } 129 130 protected static EconomicMap<OptionKey<?>, Object> osrLockNoDeopt() { 131 EconomicMap<OptionKey<?>, Object> overrides = OptionValues.newOptionMap(); 132 overrides.put(OnStackReplacementPhase.Options.DeoptAfterOSR, false); 133 overrides.put(OnStackReplacementPhase.Options.SupportOSRWithLocks, true); 134 return overrides; 135 } 136 137 protected static EconomicMap<OptionKey<?>, Object> osrLockDeopt() { 138 EconomicMap<OptionKey<?>, Object> overrides = OptionValues.newOptionMap(); 139 overrides.put(OnStackReplacementPhase.Options.SupportOSRWithLocks, true); 140 return overrides; 141 } 142 143 public static int SideEffectI; 144 145 private static void lockOnObject(Object o, String msg) { 146 Thread t = new Thread(new Runnable() { 147 @Override 148 public void run() { 149 synchronized (o) { 150 SideEffectI = 1; 151 } 152 } 153 }); 154 t.start(); 155 try { 156 t.join(1000); 157 } catch (InterruptedException e) { 158 Assert.fail("Object " + msg + " was locked"); 159 } 160 } 161 162 private static void beforeOSRLockTest() { 163 // try lock both objects 164 lockOnObject(lock, "lock"); 165 lockOnObject(lock1, "lock1"); 166 Assert.assertFalse(wasLocked()); 167 } 168 169 private static void afterOSRLockTest() { 170 // try lock both objects 171 lockOnObject(lock, "lock"); 172 lockOnObject(lock1, "lock1"); 173 Assert.assertFalse(wasLocked()); 174 // force a safepoint and hope the inflated locks are deflated 175 System.gc(); 176 } 177 178 // @Test 179 @SuppressWarnings("try") 180 public void testLockOSROuterImmediateDeoptAfter() { 181 run(() -> { 182 OptionValues options = new OptionValues(getInitialOptions(), osrLockDeopt()); 183 testOSR(options, "testOuterLockImmediateDeoptAfter"); 184 }); 185 } 186 187 static class A { 188 189 } 190 191 static class B { 192 @SuppressWarnings("unused") 193 B(A a) { 194 195 } 196 } 197 198 HashMap<String, HashSet<A>> listeners = new HashMap<>(); 199 200 public synchronized ReturnValue synchronizedSnippet() { 201 /* 202 * Test method for which liveness would conclude the original object is no longer alive 203 * although it is. 204 */ 205 Collection<HashSet<A>> allListeners = listeners.values(); 206 for (HashSet<A> group : allListeners) { 207 GraalDirectives.blackhole(group); 208 } 209 return ReturnValue.SUCCESS; 210 } 211 212 @Test 213 @SuppressWarnings("try") 214 public void testSynchronizedSnippet() { 215 GraalOSRLockTest instance = new GraalOSRLockTest(); 216 // enough entries to trigger OSR 217 for (int i = 0; i < 100000; i++) { 218 instance.listeners.put("hello" + i, null); 219 } 220 testOSR(getInitialOptions(), "synchronizedSnippet", instance); 221 Assert.assertFalse(isMonitorLockHeld(instance)); 222 } 223 224 @Test 225 @SuppressWarnings("try") 226 public void testOSRTrivialLoop() { 227 run(() -> { 228 OptionValues options = new OptionValues(getInitialOptions(), osrLockDeopt()); 229 try { 230 testOSR(options, "testReduceOSRTrivialLoop"); 231 } catch (Throwable t) { 232 Assert.assertEquals("OSR compilation without OSR entry loop.", t.getMessage()); 233 } 234 }); 235 } 236 237 @Test 238 @SuppressWarnings("try") 239 public void testLockOSROuterInnerImmediateDeoptAfter() { 240 run(() -> { 241 OptionValues options = new OptionValues(getInitialOptions(), osrLockDeopt()); 242 testOSR(options, "testOuterInnerLockImmediateDeoptAfter"); 243 }); 244 } 245 246 @Test 247 @SuppressWarnings("try") 248 public void testLockOSROuterCompileRestOfMethod() { 249 run(() -> { 250 EconomicMap<OptionKey<?>, Object> overrides = osrLockNoDeopt(); 251 overrides.put(HighTier.Options.Inline, false); 252 OptionValues options = new OptionValues(getInitialOptions(), overrides); 253 testOSR(options, "testOuterLockCompileRestOfMethod"); 254 }); 255 } 256 257 @Test 258 @SuppressWarnings("try") 259 public void testLockOSROuterInnerCompileRestOfMethod() { 260 run(() -> { 261 OptionValues options = new OptionValues(getInitialOptions(), osrLockNoDeopt()); 262 testOSR(options, "testOuterInnerLockCompileRestOfMethod"); 263 }); 264 } 265 266 @Test 267 @SuppressWarnings("try") 268 public void testLockOSROuterInnerLockDepthCompileRestOfMethod() { 269 run(() -> { 270 EconomicMap<OptionKey<?>, Object> overrides = osrLockNoDeopt(); 271 overrides.put(HighTier.Options.Inline, false); 272 OptionValues options = new OptionValues(getInitialOptions(), overrides); 273 testOSR(options, "testOuterInnerLockDepth1CompileRestOfMethod"); 274 }); 275 } 276 277 @Test 278 @SuppressWarnings("try") 279 public void testLockOSROuterInnerLockDepthDeopt() { 280 run(() -> { 281 EconomicMap<OptionKey<?>, Object> overrides = osrLockNoDeopt(); 282 overrides.put(HighTier.Options.Inline, false); 283 OptionValues options = new OptionValues(getInitialOptions(), overrides); 284 testOSR(options, "testOuterInnerLockDepth1DeoptAfter"); 285 }); 286 } 287 288 @Test 289 @SuppressWarnings("try") 290 public void testLockOSROuterInnerLockDepthRecursiveCompileRestOfMethod0() { 291 run(() -> { 292 OptionValues options = new OptionValues(getInitialOptions(), osrLockNoDeopt()); 293 testOSR(options, "testOuterInnerLockDepth1RecursiveCompileRestOfMethod1"); 294 }); 295 } 296 297 @Test 298 @SuppressWarnings("try") 299 public void testLockOSROuterInnerLockDepthRecursiveCompileRestOfMethod1() { 300 run(() -> { 301 OptionValues options = new OptionValues(getInitialOptions(), osrLockNoDeopt()); 302 testOSR(options, "testOuterInnerLockDepth1RecursiveCompileRestOfMethod2"); 303 }); 304 } 305 306 @Test 307 @SuppressWarnings("try") 308 public void testLockOSROuterCompileRestOfMethodSubsequentLock() { 309 run(() -> { 310 OptionValues options = new OptionValues(getInitialOptions(), osrLockNoDeopt()); 311 testOSR(options, "testOuterLockCompileRestOfMethodSubsequentLock"); 312 }); 313 } 314 315 @Test 316 @SuppressWarnings("try") 317 public void testLockOSROuterInnerSameLockCompileRestOfMethod() { 318 run(() -> { 319 OptionValues options = new OptionValues(getInitialOptions(), osrLockNoDeopt()); 320 testOSR(options, "testOuterInnerSameLockCompileRestOfMethod"); 321 }); 322 } 323 324 @Test 325 @SuppressWarnings("try") 326 public void testLockOSRRecursive() { 327 run(() -> { 328 // call it 329 testRecursiveLockingLeaf(); 330 ResolvedJavaMethod leaf = getResolvedJavaMethod("testRecursiveLockingLeaf"); 331 // profile it 332 leaf.reprofile(); 333 testRecursiveLockingLeaf(); 334 EconomicMap<OptionKey<?>, Object> overrides = osrLockNoDeopt(); 335 overrides.put(HighTier.Options.Inline, false); 336 OptionValues options = new OptionValues(getInitialOptions(), overrides); 337 DebugContext debug = getDebugContext(options); 338 compile(debug, leaf, -1); 339 testOSR(options, "testRecursiveLockingRoot"); 340 }); 341 } 342 343 @Test 344 @SuppressWarnings("try") 345 public void testLockOSRRecursiveLeafOSR() { 346 run(() -> { 347 testRecursiveRootNoOSR(); 348 ResolvedJavaMethod root = getResolvedJavaMethod("testRecursiveRootNoOSR"); 349 EconomicMap<OptionKey<?>, Object> overrides = osrLockNoDeopt(); 350 overrides.put(HighTier.Options.Inline, false); 351 OptionValues options = new OptionValues(getInitialOptions(), overrides); 352 DebugContext debug = getDebugContext(options); 353 compile(debug, root, -1); 354 testOSR(options, "testRecursiveLeafOSR"); 355 // force a safepoint and hope the inflated locks are deflated 356 System.gc(); 357 // call the root to call into the leaf and enter the osr-ed code 358 testRecursiveRootNoOSR(); 359 360 }); 361 } 362 363 protected static int limit = 10000; 364 protected static Object lock = new Object(); 365 protected static Object lock1 = new Object(); 366 private static final boolean LOG = false; 367 368 static { 369 // force identity hash code for easy displaced mark identification 370 int h1 = System.identityHashCode(lock); 371 int h2 = System.identityHashCode(lock1); 372 if (LOG) { 373 TTY.println("Forcing a system identity hashcode on lock object " + h1); 374 TTY.println("Forcing a system identity hashcode on lock1 object " + h2); 375 } 376 } 377 378 public static ReturnValue testReduceOSRTrivialLoop() { 379 for (int i = 0; i < limit * limit; i++) { 380 GraalDirectives.blackhole(i); 381 if (GraalDirectives.inCompiledCode()) { 382 return ReturnValue.SUCCESS; 383 } 384 } 385 return ReturnValue.FAILURE; 386 } 387 388 public static ReturnValue testOuterLockImmediateDeoptAfter() { 389 ReturnValue ret = ReturnValue.FAILURE; 390 synchronized (lock) { 391 for (int i = 1; i < 10 * limit; i++) { 392 GraalDirectives.blackhole(i); 393 if (i % 33 == 0) { 394 ret = ReturnValue.SUCCESS; 395 if (GraalDirectives.inCompiledCode() && i + 33 > (10 * limit)) { 396 GraalDirectives.blackhole(ret); 397 } 398 } 399 } 400 GraalDirectives.controlFlowAnchor(); 401 if (GraalDirectives.inCompiledCode()) { 402 throw new Error("Must not be part of compiled code"); 403 } 404 return ret; 405 } 406 } 407 408 public static ReturnValue testOuterInnerLockImmediateDeoptAfter() { 409 ReturnValue ret = ReturnValue.FAILURE; 410 synchronized (lock) { 411 for (int i = 1; i < 10 * limit; i++) { 412 synchronized (lock1) { 413 GraalDirectives.blackhole(i); 414 if (i % 33 == 0) { 415 ret = ReturnValue.SUCCESS; 416 if (GraalDirectives.inCompiledCode() && i + 33 > (10 * limit)) { 417 GraalDirectives.blackhole(ret); 418 } 419 } 420 } 421 } 422 GraalDirectives.controlFlowAnchor(); 423 GraalDirectives.deoptimize(); 424 return ret; 425 } 426 } 427 428 public static ReturnValue testOuterLockCompileRestOfMethod() { 429 ReturnValue ret = ReturnValue.FAILURE; 430 synchronized (lock) { 431 for (int i = 1; i < limit; i++) { 432 GraalDirectives.blackhole(i); 433 if (i % 1001 == 0) { 434 ret = ReturnValue.SUCCESS; 435 System.gc(); 436 } 437 } 438 return ret; 439 } 440 } 441 442 public static ReturnValue testOuterInnerLockCompileRestOfMethod() { 443 ReturnValue ret = ReturnValue.FAILURE; 444 synchronized (lock) { 445 for (int i = 1; i < 10 * limit; i++) { 446 synchronized (lock1) { 447 GraalDirectives.blackhole(i); 448 if (i % 33 == 0) { 449 ret = ReturnValue.SUCCESS; 450 if (GraalDirectives.inCompiledCode() && i + 33 > (10 * limit)) { 451 GraalDirectives.blackhole(ret); 452 System.gc(); 453 } 454 } 455 } 456 } 457 GraalDirectives.controlFlowAnchor(); 458 if (!GraalDirectives.inCompiledCode()) { 459 throw new Error("Must be part of compiled code"); 460 } 461 return ret; 462 } 463 } 464 465 public static ReturnValue testOuterInnerLockDepth1CompileRestOfMethod() { 466 // testing the order of the lock releasing 467 ReturnValue ret = ReturnValue.FAILURE; 468 synchronized (lock) { 469 synchronized (lock1) { 470 for (int i = 1; i < 10 * limit; i++) { 471 GraalDirectives.blackhole(i); 472 if (i % 33 == 0) { 473 ret = ReturnValue.SUCCESS; 474 if (GraalDirectives.inCompiledCode() && i + 33 > (10 * limit)) { 475 GraalDirectives.blackhole(ret); 476 System.gc(); 477 } 478 } 479 } 480 } 481 GraalDirectives.controlFlowAnchor(); 482 if (!GraalDirectives.inCompiledCode()) { 483 throw new Error("Must be part of compiled code already hereeeeee"); 484 } else { 485 // lock 1 must be free 486 if (isMonitorLockHeld(lock1)) { 487 throw new Error("Lock 1 must have been released already"); 488 } 489 490 // lock 2 must still be locked and cannot be acquired by another thread 491 if (!isMonitorLockHeldByThread(lock, Thread.currentThread())) { 492 throw new Error("Lock must not have been released already"); 493 } 494 } 495 return ret; 496 } 497 } 498 499 public static ReturnValue testOuterInnerLockDepth1DeoptAfter() { 500 // testing the order of the lock releasing 501 ReturnValue ret = ReturnValue.FAILURE; 502 synchronized (lock) { 503 synchronized (lock1) { 504 for (int i = 1; i < 10 * limit; i++) { 505 GraalDirectives.blackhole(i); 506 if (i % 33 == 0) { 507 ret = ReturnValue.SUCCESS; 508 if (GraalDirectives.inCompiledCode() && i + 33 > (10 * limit)) { 509 GraalDirectives.blackhole(ret); 510 } 511 } 512 } 513 GraalDirectives.controlFlowAnchor(); 514 GraalDirectives.deoptimize(); 515 if (GraalDirectives.inCompiledCode()) { 516 throw new Error("Must not part of compiled code"); 517 } 518 } 519 } 520 return ret; 521 } 522 523 public static ReturnValue testOuterInnerLockDepth1RecursiveCompileRestOfMethod1() { 524 // testing the order of the lock releasing 525 ReturnValue ret = ReturnValue.FAILURE; 526 synchronized (lock) { 527 synchronized (lock) { 528 for (int i = 1; i < 10 * limit; i++) { 529 GraalDirectives.blackhole(i); 530 if (i % 33 == 0) { 531 ret = ReturnValue.SUCCESS; 532 if (GraalDirectives.inCompiledCode() && i + 33 > (10 * limit)) { 533 GraalDirectives.blackhole(ret); 534 } 535 } 536 } 537 } 538 GraalDirectives.controlFlowAnchor(); 539 if (!GraalDirectives.inCompiledCode()) { 540 throw new Error("Must be part of compiled code"); 541 } 542 return ret; 543 } 544 } 545 546 public static ReturnValue testOuterInnerLockDepth1RecursiveCompileRestOfMethod2() { 547 // testing the order of the lock releasing 548 final Object l = lock; 549 ReturnValue ret = ReturnValue.FAILURE; 550 synchronized (l) { 551 synchronized (l) { 552 for (int i = 1; i < 10 * limit; i++) { 553 GraalDirectives.blackhole(i); 554 if (i % 33 == 0) { 555 ret = ReturnValue.SUCCESS; 556 if (GraalDirectives.inCompiledCode() && i + 33 > (10 * limit)) { 557 GraalDirectives.blackhole(ret); 558 } 559 } 560 } 561 } 562 GraalDirectives.controlFlowAnchor(); 563 if (!GraalDirectives.inCompiledCode()) { 564 throw new Error("Must be part of compiled code"); 565 } 566 return ret; 567 } 568 } 569 570 public static ReturnValue testRecursiveLockingRoot() { 571 // testing the order of the lock releasing 572 final Object l = lock; 573 ReturnValue ret = ReturnValue.FAILURE; 574 synchronized (l) { 575 synchronized (l) { 576 for (int i = 1; i < limit; i++) { 577 GraalDirectives.blackhole(i); 578 testRecursiveLockingLeaf(); 579 if (i % 33 == 0) { 580 ret = ReturnValue.SUCCESS; 581 if (GraalDirectives.inCompiledCode() && i + 33 > (10 * limit)) { 582 GraalDirectives.blackhole(ret); 583 } 584 } 585 } 586 } 587 GraalDirectives.controlFlowAnchor(); 588 if (!GraalDirectives.inCompiledCode()) { 589 throw new Error("Must be part of compiled code"); 590 } 591 return ret; 592 } 593 } 594 595 public static ReturnValue testRecursiveLockingLeaf() { 596 // testing the order of the lock releasing 597 final Object l = lock; 598 ReturnValue ret = ReturnValue.FAILURE; 599 synchronized (l) { 600 synchronized (l) { 601 for (int i = 1; i < limit; i++) { 602 GraalDirectives.blackhole(i); 603 if (i % 33 == 0) { 604 ret = ReturnValue.SUCCESS; 605 } 606 } 607 } 608 return ret; 609 } 610 } 611 612 public static ReturnValue testRecursiveRootNoOSR() { 613 // testing the order of the lock releasing 614 final Object l = lock; 615 synchronized (l) { 616 ReturnValue ret = ReturnValue.FAILURE; 617 for (int i = 0; i < 5; i++) { 618 if (GraalDirectives.inCompiledCode()) { 619 ret = testRecursiveLeafOSR(); 620 } 621 GraalDirectives.controlFlowAnchor(); 622 if (ret == ReturnValue.FAILURE) { 623 return ret; 624 } 625 } 626 GraalDirectives.controlFlowAnchor(); 627 return ret; 628 } 629 } 630 631 public static ReturnValue testRecursiveLeafOSR() { 632 ReturnValue ret = ReturnValue.FAILURE; 633 // lock is already locked by the caller 634 synchronized (lock) { 635 for (int i = 1; i < 10 * limit; i++) { 636 GraalDirectives.blackhole(i); 637 if (i % 33 == 0) { 638 ret = ReturnValue.SUCCESS; 639 if (GraalDirectives.inCompiledCode() && i + 33 > (10 * limit)) { 640 GraalDirectives.blackhole(ret); 641 } 642 } 643 } 644 GraalDirectives.controlFlowAnchor(); 645 return ret; 646 } 647 } 648 649 // test cases for optimizations 650 public static ReturnValue testOuterLockCompileRestOfMethodSubsequentLock() { 651 final Object monitor = lock; 652 ReturnValue ret = ReturnValue.FAILURE; 653 synchronized (monitor) { 654 for (int i = 1; i < 10 * limit; i++) { 655 GraalDirectives.blackhole(i); 656 if (i % 33 == 0) { 657 ret = ReturnValue.SUCCESS; 658 if (GraalDirectives.inCompiledCode() && i + 33 > (10 * limit)) { 659 GraalDirectives.blackhole(ret); 660 } 661 } 662 } 663 } 664 synchronized (monitor) { 665 GraalDirectives.controlFlowAnchor(); 666 if (!GraalDirectives.inCompiledCode()) { 667 throw new Error("Must be part of compiled code"); 668 } 669 } 670 return ret; 671 672 } 673 674 public static ReturnValue testOuterInnerSameLockCompileRestOfMethod() { 675 final Object monitor = lock; 676 ReturnValue ret = ReturnValue.FAILURE; 677 synchronized (monitor) { 678 for (int i = 1; i < 10 * limit; i++) { 679 synchronized (monitor) { 680 GraalDirectives.blackhole(i); 681 if (i % 33 == 0) { 682 ret = ReturnValue.SUCCESS; 683 if (GraalDirectives.inCompiledCode() && i + 33 > (10 * limit)) { 684 GraalDirectives.blackhole(ret); 685 } 686 } 687 } 688 } 689 GraalDirectives.controlFlowAnchor(); 690 if (!GraalDirectives.inCompiledCode()) { 691 throw new Error("Must be part of compiled code"); 692 } 693 return ret; 694 } 695 } 696 697 }