1 /*
   2 * Copyright (c) 2018, 2023, 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  * @test id=default
  26  * @key randomness
  27  * @summary Fuzz tests for jdk.internal.vm.Continuation
  28  * @requires vm.continuations
  29  * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4)
  30  * @requires vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == true
  31  * @modules java.base java.base/jdk.internal.vm.annotation java.base/jdk.internal.vm
  32  * @library /test/lib
  33  * @build java.base/java.lang.StackWalkerHelper
  34  * @build jdk.test.whitebox.WhiteBox
  35  * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
  36  *
  37  * @run main/othervm/timeout=300 -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
  38  *                               Fuzz
  39  */
  40 
  41 /*
  42  * @test id=preserve-fp
  43  * @key randomness
  44  * @summary Fuzz tests for jdk.internal.vm.Continuation
  45  * @requires vm.continuations
  46  * @requires vm.flavor == "server" & (vm.opt.TieredStopAtLevel == null | vm.opt.TieredStopAtLevel == 4)
  47  * @requires vm.opt.TieredCompilation == null | vm.opt.TieredCompilation == true
  48  * @modules java.base java.base/jdk.internal.vm.annotation java.base/jdk.internal.vm
  49  * @library /test/lib
  50  * @build java.base/java.lang.StackWalkerHelper
  51  * @build jdk.test.whitebox.WhiteBox
  52  * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
  53  *
  54  * @run main/othervm/timeout=300 -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:.
  55  *                               -XX:+PreserveFramePointer
  56  *                               Fuzz
  57  */
  58 
  59 import jdk.internal.vm.Continuation;
  60 import jdk.internal.vm.ContinuationScope;
  61 
  62 import java.lang.invoke.*;
  63 import java.lang.reflect.*;
  64 import java.lang.StackWalker.StackFrame;
  65 import java.nio.file.*;
  66 import java.util.*;
  67 import java.util.function.*;
  68 import java.util.stream.*;
  69 import static java.lang.Math.max;
  70 import static java.lang.Math.min;
  71 import jdk.internal.vm.annotation.DontInline;
  72 import jdk.test.lib.Utils;
  73 import jdk.test.whitebox.WhiteBox;
  74 
  75 import jdk.test.lib.Platform;
  76 import jtreg.SkippedException;
  77 
  78 public class Fuzz implements Runnable {
  79     static final boolean VERIFY_STACK = true; // could add significant time
  80     static final boolean FILE    = true;
  81     static final boolean RANDOM  = true;
  82     static final boolean VERBOSE = false;
  83 
  84     static float timeoutFactor = Float.parseFloat(System.getProperty("test.timeout.factor", "1.0"));
  85     static int COMPILATION_TIMEOUT = (int)(5_000 * timeoutFactor); // ms
  86 
  87     static final Path TEST_DIR = Path.of(System.getProperty("test.src", "."));
  88 
  89     public static void main(String[] args) {
  90         if (Platform.isSlowDebugBuild() && Platform.isOSX() && Platform.isAArch64()) {
  91             throw new SkippedException("Test is unstable with slowdebug bits "
  92                                        + "on macosx-aarch64");
  93         }
  94         if (Platform.isPPC()) {
  95             COMPILATION_TIMEOUT = COMPILATION_TIMEOUT * 2;
  96         }
  97         warmup();
  98         for (int compileLevel : new int[]{4}) {
  99             for (boolean compileRun : new boolean[]{true}) {
 100                 COMPILE_LEVEL = compileLevel;
 101                 COMPILE_RUN   = compileRun;
 102                 resetCompilation();
 103                 runTests();
 104             }
 105         }
 106     }
 107 
 108     static void runTests() {
 109         if (FILE)   testFile("fuzz.dat");
 110         if (RANDOM) testRandom(System.currentTimeMillis(), 50);
 111     }
 112 
 113     ////////////////
 114 
 115     enum Op {
 116         CALL_I_INT, CALL_I_DBL, CALL_I_MANY,
 117         CALL_C_INT, CALL_C_DBL, CALL_C_MANY,
 118         CALL_I_CTCH, CALL_C_CTCH,
 119         CALL_I_PIN, CALL_C_PIN,
 120         MH_I_INT, MH_C_INT, MH_I_MANY, MH_C_MANY,
 121         REF_I_INT, REF_C_INT, REF_I_MANY, REF_C_MANY,
 122         LOOP, YIELD, THROW, DONE;
 123 
 124         static final EnumSet<Op> BASIC       = EnumSet.of(LOOP, YIELD);
 125         static final EnumSet<Op> STANDARD    = EnumSet.range(CALL_I_INT, CALL_C_CTCH);
 126         static final EnumSet<Op> PIN         = EnumSet.range(CALL_I_PIN, CALL_C_PIN);
 127         static final EnumSet<Op> MH          = EnumSet.range(MH_I_INT, MH_C_MANY);
 128         static final EnumSet<Op> REFLECTED   = EnumSet.range(REF_I_INT, REF_C_MANY);
 129         static final EnumSet<Op> NON_CALLS   = EnumSet.range(LOOP, DONE);
 130         static final EnumSet<Op> COMPILED    = EnumSet.copyOf(Arrays.stream(Op.values()).filter(x -> x.toString().contains("_C_")).collect(Collectors.toList()));
 131         static final EnumSet<Op> INTERPRETED = EnumSet.copyOf(Arrays.stream(Op.values()).filter(x -> x.toString().contains("_I_")).collect(Collectors.toList()));
 132 
 133         static Op toInterpreted(Op op) { return INTERPRETED.contains(op) ? op : Enum.valueOf(Op.class, op.toString().replace("_C_", "_I_")); }
 134         static Op toCompiled(Op op)    { return COMPILED.contains(op)    ? op : Enum.valueOf(Op.class, op.toString().replace("_I_", "_C_")); }
 135     }
 136 
 137     static class Generator {
 138         public Op[] generate() {
 139             final int length = max(1, pick(5, 10, 50/*, 200*/) + plusOrMinus(5));
 140 
 141             Set<Op> highProb = new HashSet<Op>();
 142             Set<Op> lowProb  = new HashSet<Op>();
 143 
 144             if (percent(100)) highProb.addAll(Op.BASIC);
 145             if (percent(100)) highProb.addAll(Op.STANDARD);
 146             if (percent(1)) lowProb.add(Op.THROW);
 147             if (percent(3)) lowProb.addAll(Op.PIN);
 148             if (percent(3)) lowProb.addAll(Op.MH);
 149             if (percent(0)) lowProb.addAll(Op.REFLECTED);
 150             if (percent(50)) {
 151                 highProb.removeAll(Op.INTERPRETED);
 152                 lowProb.removeAll(Op.INTERPRETED);
 153             }
 154             Op[] highProb0 = highProb.toArray(Op[]::new);
 155             Op[] lowProb0  = lowProb.toArray(Op[]::new);
 156 
 157             int loops = 7;
 158             Op[] trace = new Op[length];
 159             for (int i=0; i < trace.length; i++) {
 160                 trace[i] = pick((lowProb.isEmpty() || percent(90)) ? highProb0 : lowProb0);
 161                 if (trace[i] == Op.LOOP && (loops--) <= 0) i--;
 162             }
 163             return trace;
 164         }
 165 
 166         private final Random rnd;
 167         public Generator(Random rnd) { this.rnd = rnd; }
 168         @SafeVarargs
 169         private <T> T pick(T... values) { return values[rnd.nextInt(values.length)]; }
 170         private boolean percent(int percent) { return rnd.nextInt(100) < percent; }
 171         private int plusOrMinus(int n) { return rnd.nextInt(2*n + 1) - n; }
 172     }
 173 
 174     static Stream<Op[]> random(Random rnd) {
 175         var g = new Generator(rnd);
 176         return Stream.iterate(0, x->x+1).map(__ -> g.generate());
 177     }
 178 
 179     static void testRandom(long seed, int number) {
 180         System.out.println("-- RANDOM (seed: " + seed + ") --");
 181         testStream(random(new Random(seed)).limit(number));
 182     }
 183 
 184     static void testFile(String fileName) {
 185         System.out.println("-- FILE (" + fileName + ") --");
 186         try {
 187             testStream(file(TEST_DIR.resolve(fileName)));
 188         } catch (java.io.IOException e) { throw new RuntimeException(e); }
 189     }
 190 
 191     static Stream<Op[]> file(Path file) throws java.io.IOException {
 192         return Files.lines(file).map(String::trim).filter(s -> !s.isBlank() && !s.startsWith("#")).map(Fuzz::parse);
 193     }
 194 
 195     static Op[] parse(String line) {
 196         return Arrays.stream(line.split(", ")).map(s -> Enum.valueOf(Op.class, s))
 197             .collect(Collectors.toList()).toArray(Op[]::new);
 198     }
 199 
 200     static int testCounter;
 201 
 202     static void testStream(Stream<Op[]> traces) { testCounter = 0; traces.forEach(Fuzz::testTrace); }
 203 
 204     ////////////////////////////////////////
 205 
 206     static void testTrace(Op[] trace) {
 207         testCounter++;
 208         System.out.println("\n" + testCounter + ": COMPILE_LEVEL: " + COMPILE_LEVEL + " COMPILE_RUN: " + COMPILE_RUN);
 209         for (int attempt = 0; attempt < 3; attempt++) {
 210             if (attempt > 0) System.out.println("RETRYING " + attempt);
 211 
 212             compile();
 213 
 214             long start = time();
 215             var fuzz = new Fuzz(trace);
 216             fuzz.verbose = VERBOSE && attempt == 0;
 217             fuzz.print();
 218             int yields = fuzz.test();
 219             time(start, "Test (" + yields + " yields)");
 220 
 221             if (fuzz.checkCompilation())
 222                 break;
 223         }
 224     }
 225 
 226     static final ContinuationScope SCOPE = new ContinuationScope() {};
 227 
 228     static class FuzzException extends RuntimeException {
 229         public FuzzException(String msg) { super(msg); }
 230     }
 231 
 232     boolean verbose = false;
 233 
 234     private final Op[] trace;
 235     private int index  = -1;
 236     private int result = -1;
 237 
 238     private Fuzz(Op[] trace) { this.trace = trace; }
 239 
 240     int test() {
 241         Continuation cont = new Continuation(SCOPE, this) {
 242             @Override protected void onPinned(Pinned reason) { if (verbose) System.out.println("PINNED " + reason); }
 243         };
 244 
 245         this.yields = 0;
 246         int count = 0;
 247         try {
 248             while (true) {
 249                 var start = time();
 250                 cont.run();
 251                 if (cont.isDone()) break;
 252 
 253                 assert !shouldThrow();
 254                 verifyStack(cont);
 255                 count++;
 256                 time(start, "Iteration");
 257             }
 258             verifyResult(result);
 259         } catch (FuzzException e) {
 260             assert shouldThrow();
 261             assert e.getMessage().equals("EX");
 262             assert cont.isDone();
 263         }
 264         assert count == yields : "count: " + count + " yields: " + yields;
 265         return count;
 266     }
 267 
 268     void print() { printTrace(trace); }
 269 
 270     private Op trace(int i) { return i < trace.length ? trace[i] : Op.DONE; }
 271     private Op current()    { return trace(index); }
 272     private Op next(int c)  { logOp(c); index++; return current(); }
 273 
 274     ////// Compilation
 275 
 276     private static boolean COMPILE_RUN;
 277     private static int COMPILE_LEVEL;
 278 
 279     static final int  WARMUP_ITERS = 15_000;
 280     static final Op[] WARMUP_TRACE = {Op.MH_C_INT, Op.MH_C_MANY, Op.REF_C_INT, Op.REF_C_MANY, Op.CALL_C_INT};
 281 
 282     static void warmup() {
 283         final long start = time();
 284         warmup(WARMUP_TRACE, WARMUP_ITERS); // generate (for reflection) and compile method handles
 285         time(start, "Warmup");
 286     }
 287 
 288     static void warmup(Op[] trace, int times) {
 289         for (int i=0; i<times; i++) {
 290             new Fuzz(trace).run();
 291         }
 292     }
 293 
 294     static void resetCompilation() {
 295         Set<Method> compile = Op.COMPILED.stream().map(Fuzz::method).collect(Collectors.toCollection(HashSet::new));
 296         compile.add(run);
 297 
 298         for (Method m : compile) {
 299             WB.deoptimizeMethod(m);
 300             WB.clearMethodState(m);
 301         }
 302     }
 303 
 304     static void enqueueForCompilation(Method m) {
 305         if (WB.isMethodCompiled(m)) return;
 306         // WB compilation tasks do not expire while others do,
 307         // so we wait for an existing task to finish before enqueuing.
 308         // Alternatively run with -XX:TieredCompileTaskTimeout=5000
 309         Utils.waitForCondition(() -> WB.isMethodQueuedForCompilation(m), 1000);
 310         if (WB.isMethodCompiled(m)) return;
 311         WB.enqueueMethodForCompilation(m, COMPILE_LEVEL);
 312     }
 313 
 314     static void waitForCompilation(Method m) {
 315         if (!Utils.waitForCondition(() -> WB.isMethodCompiled(m), COMPILATION_TIMEOUT)) {
 316             System.out.println(">>> Compilation status for: " + m);
 317             System.out.println("isMethodCompiled: " + WB.isMethodCompiled(m) + " " +
 318                                 "isMethodCompilable: " + WB.isMethodCompilable(m) + " " +
 319                                 "isMethodQueuedForCompilation: " + WB.isMethodQueuedForCompilation(m) + " " +
 320                                 "getMethodCompilationLevel: " + WB.getMethodCompilationLevel(m));
 321             throw new AssertionError("Failed to compile " + m + " in " + COMPILATION_TIMEOUT + "ms");
 322         }
 323     }
 324 
 325     static void compileContinuation() {
 326         var compile = new HashSet<Method>();
 327         for (Method m : Continuation.class.getDeclaredMethods()) {
 328             if (!WB.isMethodCompiled(m)) {
 329                 if (!Modifier.isNative(m.getModifiers())
 330                     && (m.getName().startsWith("enter")
 331                      || m.getName().startsWith("yield"))) {
 332                     enqueueForCompilation(m);
 333                     compile.add(m);
 334                 }
 335             }
 336         }
 337 
 338         for (Method m : compile) waitForCompilation(m);
 339     }
 340 
 341     static void compile() {
 342         final long start = time();
 343 
 344         compileContinuation();
 345 
 346         Set<Method> compile   =    Op.COMPILED.stream().map(Fuzz::method).collect(Collectors.toCollection(HashSet::new));
 347         Set<Method> interpret = Op.INTERPRETED.stream().map(Fuzz::method).collect(Collectors.toCollection(HashSet::new));
 348         (COMPILE_RUN ? compile : interpret).add(run);
 349 
 350         compile.addAll(precompile);
 351 
 352         for (Method m : interpret) WB.makeMethodNotCompilable(m);
 353 
 354         for (Method m : compile)   enqueueForCompilation(m);
 355         for (Method m : compile)   waitForCompilation(m);
 356         for (Method m : compile)   assert  WB.isMethodCompiled(m) : "method: " + m;
 357         for (Method m : interpret) assert !WB.isMethodCompiled(m) : "method: " + m;
 358 
 359         time(start, "Compile");
 360     }
 361 
 362     boolean checkContinuationCompilation() {
 363         for (Method m : Continuation.class.getDeclaredMethods()) {
 364             if (!WB.isMethodCompiled(m)) {
 365                 if (!Modifier.isNative(m.getModifiers())
 366                     && (m.getName().startsWith("enter")
 367                      || m.getName().startsWith("yield"))) {
 368                     return false;
 369                 }
 370             }
 371         }
 372         return true;
 373     }
 374 
 375     boolean checkCompilation() {
 376         boolean res = true;
 377 
 378         if (!checkContinuationCompilation()) {
 379             res = false;
 380             System.out.println("CHANGED CONTINUATION COMPILATION");
 381         }
 382 
 383         Op[] newTrace = Arrays.copyOf(trace, trace.length);
 384         if (!checkCompilation(newTrace)) {
 385             res = false;
 386             System.out.println("CHANGED COMPILATION");
 387             printTrace(newTrace);
 388         }
 389 
 390         return res;
 391     }
 392 
 393     static boolean checkCompilation(Op[] trace) {
 394         boolean ok = true;
 395         for (int i = 0; i < trace.length; i++) {
 396             Op op = trace[i];
 397             if (Op.COMPILED.contains(op)    && !WB.isMethodCompiled(method(op))) trace[i] = Op.toInterpreted(op);
 398             if (Op.INTERPRETED.contains(op) &&  WB.isMethodCompiled(method(op))) trace[i] = Op.toCompiled(op);
 399             if (op != trace[i]) ok = false;
 400         }
 401         return ok;
 402     }
 403 
 404     /////////// Instance Helpers
 405 
 406     private StackTraceElement[] backtrace;
 407     private StackFrame[] fbacktrace;
 408     private StackFrame[] lfbacktrace;
 409     private int yields;
 410 
 411     void indent(int depth) {
 412         // depth = index;
 413         for (int i=0; i<depth; i++) System.out.print("  ");
 414     }
 415 
 416     void logOp(int iter) {
 417         if (!verbose) return;
 418 
 419         int depth = depth();
 420         System.out.print("> " + depth + " ");
 421         indent(depth);
 422         System.out.println("iter: " + iter + " index: " + index + " op: " + trace(index+1));
 423     }
 424 
 425     <T> T log(T result) {
 426         if (!verbose) return result;
 427 
 428         int depth = depth();
 429         System.out.print("> " + depth + " ");
 430         indent(depth);
 431         System.out.println("result " + result);
 432         return result;
 433     }
 434 
 435     int depth() {
 436         int d = 0;
 437         for (int i=0; i<=index && i < trace.length; i++) if (!Op.NON_CALLS.contains(trace[i])) d++;
 438         return d;
 439     }
 440 
 441     boolean traceHas(Predicate<Op> pred) {
 442         for (int i = 0; i < index; i++) if (pred.test(trace[i])) return true;
 443         return false;
 444     }
 445 
 446     String[] expectedStackTrace() {
 447         var ms = new ArrayList<String>();
 448         for (int i = index; i >= 0; i--) if (!Op.NON_CALLS.contains(trace[i])) ms.add(method(trace[i]).getName());
 449         ms.add("run");
 450         return ms.toArray(new String[0]);
 451     }
 452 
 453     int computeResult() {
 454         // To compute the expected result, we remove all YIELDs from the trace and run it
 455         Op[] trace0 = Arrays.stream(trace).filter(op -> op != Op.YIELD)
 456             .collect(Collectors.toList()).toArray(Op[]::new);
 457 
 458         Fuzz f0 = new Fuzz(trace0);
 459         // if (VERBOSE) {
 460         //     System.out.println(">>>> RESULT");
 461         //     f0.verbose = true;
 462         // }
 463         f0.run();
 464         return f0.result;
 465     }
 466 
 467     void verifyResult(int result) {
 468         int computed = computeResult();
 469         assert result == computed : "result: " + result + " expected: " + computed;
 470     }
 471 
 472     boolean shouldPin() {
 473         return traceHas(Op.PIN::contains);
 474     }
 475 
 476     void verifyPin(boolean yieldResult) {
 477         if (yieldResult) yields++;
 478         if (!yieldResult && traceHas(op -> Op.INTERPRETED.contains(op) && Op.REFLECTED.contains(op))) return;
 479         assert yieldResult != shouldPin() : "res: " + yieldResult + " shouldPin: " + shouldPin();
 480     }
 481 
 482     boolean shouldThrow() {
 483         for (int i = 0; i <= index && i < trace.length; i++) {
 484             switch (trace[i]) {
 485                 case CALL_I_CTCH, CALL_C_CTCH -> { return false; }
 486                 case THROW -> { return true; }
 487                 default -> {}
 488             }
 489         }
 490         return false;
 491     }
 492 
 493     void captureStack() {
 494         // Thread.dumpStack();
 495         if (!VERIFY_STACK) return;
 496         backtrace = Thread.currentThread().getStackTrace();
 497         fbacktrace = StackWalkerHelper.getStackFrames(SCOPE);
 498         lfbacktrace = StackWalkerHelper.getLiveStackFrames(SCOPE);
 499     }
 500 
 501     void verifyStack() {
 502         if (!VERIFY_STACK) return;
 503         var start = time();
 504         verifyStack(backtrace);
 505         verifyStack(backtrace, StackWalkerHelper.toStackTraceElement(fbacktrace));
 506         verifyStack(fbacktrace, lfbacktrace);
 507 
 508         verifyStack(backtrace, Thread.currentThread().getStackTrace());
 509         verifyStack(fbacktrace, StackWalkerHelper.getStackFrames(SCOPE));
 510         verifyStack(lfbacktrace, StackWalkerHelper.getLiveStackFrames(SCOPE));
 511         time(start, "Verify stack");
 512     }
 513 
 514     void verifyStack(Continuation cont) {
 515         if (!VERIFY_STACK) return;
 516         var start = time();
 517         verifyStack(backtrace);
 518         verifyStack(backtrace, StackWalkerHelper.toStackTraceElement(fbacktrace));
 519         verifyStack(fbacktrace, lfbacktrace);
 520 
 521         verifyStack(backtrace, cont.getStackTrace());
 522         verifyStack(fbacktrace, StackWalkerHelper.getStackFrames(cont));
 523         verifyStack(lfbacktrace, StackWalkerHelper.getLiveStackFrames(cont));
 524         time(start, "Verify continuation stack");
 525     }
 526 
 527     static boolean isStackCaptureMechanism(Object sf) {
 528         return Fuzz.class.getName().equals(sfClassName(sf))
 529             && ("captureStack".equals(sfMethodName(sf)) || "verifyStack".equals(sfMethodName(sf)));
 530     }
 531 
 532     static boolean isPrePostYield(Object sf) {
 533         return Fuzz.class.getName().equals(sfClassName(sf))
 534             && ("preYield".equals(sfMethodName(sf)) || "postYield".equals(sfMethodName(sf)));
 535     }
 536 
 537     static <T> T[] cutStack(T[] stack) {
 538         var list = new ArrayList<T>();
 539         int i = 0;
 540         while (i < stack.length && (!Fuzz.class.getName().equals(sfClassName(stack[i])) || isPrePostYield(stack[i]) || isStackCaptureMechanism(stack[i]))) i++;
 541         while (i < stack.length && !Continuation.class.getName().equals(sfClassName(stack[i]))) { list.add(stack[i]); i++; }
 542         // while (i < stack.length && Continuation.class.getName().equals(sfClassName(stack[i])) && !"enterSpecial".equals(sfMethodName(stack[i]))) { list.add(stack[i]); i++; }
 543         return list.toArray(arrayType(stack));
 544     }
 545 
 546     void verifyStack(Object[] observed) {
 547         verifyStack(
 548             expectedStackTrace(),
 549             Arrays.stream(cutStack(observed)).filter(sf -> Fuzz.class.getName().equals(sfClassName(sf)))
 550                             .collect(Collectors.toList()).toArray(Object[]::new));
 551     }
 552 
 553     static void verifyStack(Object[] expected, Object[] observed) {
 554         expected = cutStack(expected);
 555         observed = cutStack(observed);
 556         boolean equal = true;
 557         if (expected.length == observed.length) {
 558             for (int i=0; i < expected.length; i++) {
 559                 if (!sfEquals(expected[i], observed[i])) {
 560                     // we allow a different line number for the first element
 561                     if (i > 0 || !Objects.equals(sfClassName(expected[i]), sfClassName(observed[i])) || !Objects.equals(sfMethodName(expected[i]), sfMethodName(observed[i]))) {
 562                         System.out.println("At index " + i + " expected: " + sfToString(expected[i]) + " observed: " + sfToString(observed[i]));
 563 
 564                         equal = false;
 565                         break;
 566                     }
 567                 }
 568             }
 569         } else {
 570             equal = false;
 571             System.out.println("Expected length: " + expected.length + " Observed length: " + observed.length);
 572         }
 573         if (!equal) {
 574             System.out.println("Expected: "); for (var sf : expected) System.out.println("\t" + sf);
 575             System.out.println("Observed: "); for (var sf : observed) System.out.println("\t" + sf);
 576         }
 577         assert equal;
 578     }
 579 
 580     static String sfClassName(Object f)  {
 581         return f instanceof String ? Fuzz.class.getName() :
 582             (f instanceof StackTraceElement ? ((StackTraceElement)f).getClassName()  : ((StackFrame)f).getClassName()); }
 583     static String sfMethodName(Object f) {
 584         return f instanceof String ? (String)f :
 585             (f instanceof StackTraceElement ? ((StackTraceElement)f).getMethodName() : ((StackFrame)f).getMethodName()); }
 586 
 587     static boolean sfEquals(Object a, Object b) {
 588         if (a instanceof String)
 589             return sfClassName(a).equals(sfClassName(b)) && sfMethodName(a).equals(sfMethodName(b));
 590 
 591         return a instanceof StackTraceElement ? Objects.equals(a, b)
 592                                               : StackWalkerHelper.equals((StackFrame)a, (StackFrame)b);
 593     }
 594 
 595     static String sfToString(Object f) {
 596         return f instanceof StackFrame ? StackWalkerHelper.frameToString((StackFrame)f) : Objects.toString(f);
 597     }
 598 
 599     //// Static Helpers
 600 
 601     static void rethrow(Throwable t) {
 602         if (t instanceof Error) throw (Error)t;
 603         if (t instanceof RuntimeException) throw (RuntimeException)t;
 604         throw new AssertionError(t);
 605     }
 606 
 607     static <T> T[] arrayType(T[] array) {
 608         return (T[])java.lang.reflect.Array.newInstance(array.getClass().componentType(), 0);
 609     }
 610 
 611     static void printTrace(Op[] trace) { System.out.println(write(trace)); }
 612 
 613     static String write(Op[] trace) {
 614         return Arrays.stream(trace).map(Object::toString).collect(Collectors.joining(", "));
 615     }
 616 
 617     static Method method(Op op)       { return method.get(op); }
 618     static MethodHandle handle(Op op) { return handle.get(op); }
 619 
 620     static long time() { return System.nanoTime(); }
 621     static void time(long startNanos, String message) {
 622         final long duration = (System.nanoTime() - startNanos)/1_000_000;
 623         if (duration > 500)
 624             System.out.println(message + " in " + duration + " ms");
 625     }
 626 
 627     //////
 628 
 629     private static final WhiteBox WB = WhiteBox.getWhiteBox();
 630 
 631     static final Class<?>[] run_sig = new Class<?>[]{};
 632     static final Class<?>[] int_sig = new Class<?>[]{int.class, int.class};
 633     static final Class<?>[] dbl_sig = new Class<?>[]{int.class, double.class};
 634     static final Class<?>[] mny_sig = new Class<?>[]{int.class,
 635         int.class, double.class, long.class, float.class, Object.class,
 636         int.class, double.class, long.class, float.class, Object.class,
 637         int.class, double.class, long.class, float.class, Object.class,
 638         int.class, double.class, long.class, float.class, Object.class};
 639     static final MethodType run_type = MethodType.methodType(void.class, run_sig);
 640     static final MethodType int_type = MethodType.methodType(int.class, int_sig);
 641     static final MethodType dbl_type = MethodType.methodType(double.class, dbl_sig);
 642     static final MethodType mny_type = MethodType.methodType(int.class, mny_sig);
 643 
 644     static final List<Method> precompile = new ArrayList<>();
 645 
 646     static final Method run;
 647     static final Map<Op, Method>       method = new EnumMap<>(Op.class);
 648     static final Map<Op, MethodHandle> handle = new EnumMap<>(Op.class);
 649 
 650     static {
 651         try {
 652             run = Fuzz.class.getDeclaredMethod("run", run_sig);
 653             // precompile.add(Fuzz.class.getDeclaredMethod("maybeResetIndex", new Class<?>[]{int.class}));
 654 
 655             method.put(Op.CALL_I_INT,  Fuzz.class.getDeclaredMethod("int_int", int_sig));
 656             method.put(Op.CALL_C_INT,  Fuzz.class.getDeclaredMethod("com_int", int_sig));
 657             method.put(Op.CALL_I_DBL,  Fuzz.class.getDeclaredMethod("int_dbl", dbl_sig));
 658             method.put(Op.CALL_C_DBL,  Fuzz.class.getDeclaredMethod("com_dbl", dbl_sig));
 659             method.put(Op.CALL_I_MANY, Fuzz.class.getDeclaredMethod("int_mny", mny_sig));
 660             method.put(Op.CALL_C_MANY, Fuzz.class.getDeclaredMethod("com_mny", mny_sig));
 661             method.put(Op.CALL_I_PIN,  Fuzz.class.getDeclaredMethod("int_pin", int_sig));
 662             method.put(Op.CALL_C_PIN,  Fuzz.class.getDeclaredMethod("com_pin", int_sig));
 663 
 664             method.put(Op.CALL_I_CTCH, method(Op.CALL_I_INT));
 665             method.put(Op.CALL_C_CTCH, method(Op.CALL_C_INT));
 666 
 667             method.put(Op.MH_I_INT,  method(Op.CALL_I_INT));
 668             method.put(Op.MH_C_INT,  method(Op.CALL_C_INT));
 669             method.put(Op.MH_I_MANY, method(Op.CALL_I_MANY));
 670             method.put(Op.MH_C_MANY, method(Op.CALL_C_MANY));
 671 
 672             method.put(Op.REF_I_INT,  method(Op.CALL_I_INT));
 673             method.put(Op.REF_C_INT,  method(Op.CALL_C_INT));
 674             method.put(Op.REF_I_MANY, method(Op.CALL_I_MANY));
 675             method.put(Op.REF_C_MANY, method(Op.CALL_C_MANY));
 676 
 677             MethodHandles.Lookup lookup = MethodHandles.lookup();
 678 
 679             handle.put(Op.MH_I_INT,  lookup.unreflect(method(Op.CALL_I_INT)));
 680             handle.put(Op.MH_C_INT,  lookup.unreflect(method(Op.CALL_C_INT)));
 681             handle.put(Op.MH_I_MANY, lookup.unreflect(method(Op.CALL_I_MANY)));
 682             handle.put(Op.MH_C_MANY, lookup.unreflect(method(Op.CALL_C_MANY)));
 683         } catch (ReflectiveOperationException e) {
 684             throw new AssertionError(e);
 685         }
 686     }
 687 
 688     @DontInline void preYield() { captureStack(); }
 689     @DontInline void postYield(boolean yieldResult) { verifyPin(yieldResult); verifyStack(); }
 690     @DontInline void maybeResetIndex(int index0) { this.index = current() != Op.YIELD ? index0 : index; }
 691     @DontInline void throwException() { throw new FuzzException("EX"); }
 692 
 693     @Override
 694     public void run() {
 695         final int depth = 0;
 696         int res = 3;
 697 
 698         int x1 = (int)res, x2 = (int)res, x3 = (int)res, x4 = (int)res;
 699         double d1 = (double)res, d2 = (double)res, d3 = (double)res, d4 = (double)res;
 700         long l1 = (long)res, l2 = (long)res, l3 = (long)res, l4 = (long)res;
 701         float f1 = (float)res, f2 = (float)res, f3 = (float)res, f4 = (float)res;
 702         Object o1 = res, o2 = res, o3 = res, o4 = res;
 703 
 704         for (int c = 1, index0 = index; c > 0; c--, maybeResetIndex(index0)) { // index0 is the index to which we return when we loop
 705             switch (next(c)) {
 706             case THROW -> throwException();
 707             case LOOP  -> { c += 2; index0 = index; }
 708             case YIELD -> { preYield(); boolean y = Continuation.yield(SCOPE); postYield(y); c++; }
 709             case DONE  -> { break; }
 710             case CALL_I_INT  -> res += int_int(depth+1, (int)res);
 711             case CALL_C_INT  -> res += com_int(depth+1, (int)res);
 712             case CALL_I_DBL  -> res += (int)int_dbl(depth+1, res);
 713             case CALL_C_DBL  -> res += (int)com_dbl(depth+1, res);
 714             case CALL_I_PIN  -> res += int_pin(depth+1, (int)res);
 715             case CALL_C_PIN  -> res += com_pin(depth+1, (int)res);
 716             case CALL_I_MANY -> res += int_mny(depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4);
 717             case CALL_C_MANY -> res += com_mny(depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4);
 718             case CALL_I_CTCH -> {try { res += int_int(depth+1, (int)res); } catch (FuzzException e) {}}
 719             case CALL_C_CTCH -> {try { res += com_int(depth+1, (int)res); } catch (FuzzException e) {}}
 720             case MH_I_INT, MH_C_INT     -> {try { res += (int)handle(current()).invokeExact(this, depth+1, (int)res);  } catch (Throwable e) { rethrow(e); }}
 721             case MH_I_MANY, MH_C_MANY   -> {try { res += (int)handle(current()).invokeExact(this, depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4); } catch (Throwable e) { rethrow(e); }}
 722             case REF_I_INT,  REF_C_INT  -> {try { res += (int)method(current()).invoke(this, depth+1, (int)res); } catch (InvocationTargetException e) { rethrow(e.getCause()); } catch (IllegalAccessException e) { assert false; }}
 723             case REF_I_MANY, REF_C_MANY -> {try { res += (int)method(current()).invoke(this, depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4); } catch (InvocationTargetException e) { rethrow(e.getCause()); } catch (IllegalAccessException e) { assert false; }}
 724             default -> throw new AssertionError("Unknown op: " + current());
 725             }
 726         }
 727 
 728         this.result = log(res);
 729     }
 730 
 731     @DontInline
 732     int int_int(final int depth, int x) {
 733         int res = x;
 734 
 735         int x1 = (int)res, x2 = (int)res, x3 = (int)res, x4 = (int)res;
 736         double d1 = (double)res, d2 = (double)res, d3 = (double)res, d4 = (double)res;
 737         long l1 = (long)res, l2 = (long)res, l3 = (long)res, l4 = (long)res;
 738         float f1 = (float)res, f2 = (float)res, f3 = (float)res, f4 = (float)res;
 739         Object o1 = res, o2 = res, o3 = res, o4 = res;
 740 
 741         for (int c = 1, index0 = index; c > 0; c--, maybeResetIndex(index0)) { // index0 is the index to which we return when we loop
 742             switch (next(c)) {
 743             case THROW -> throwException();
 744             case LOOP  -> { c += 2; index0 = index; }
 745             case YIELD -> { preYield(); boolean y = Continuation.yield(SCOPE); postYield(y); c++; }
 746             case DONE  -> { break; }
 747             case CALL_I_INT  -> res += int_int(depth+1, (int)res);
 748             case CALL_C_INT  -> res += com_int(depth+1, (int)res);
 749             case CALL_I_DBL  -> res += (int)int_dbl(depth+1, res);
 750             case CALL_C_DBL  -> res += (int)com_dbl(depth+1, res);
 751             case CALL_I_PIN  -> res += int_pin(depth+1, (int)res);
 752             case CALL_C_PIN  -> res += com_pin(depth+1, (int)res);
 753             case CALL_I_MANY -> res += int_mny(depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4);
 754             case CALL_C_MANY -> res += com_mny(depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4);
 755             case CALL_I_CTCH -> {try { res += int_int(depth+1, (int)res); } catch (FuzzException e) {}}
 756             case CALL_C_CTCH -> {try { res += com_int(depth+1, (int)res); } catch (FuzzException e) {}}
 757             case MH_I_INT, MH_C_INT     -> {try { res += (int)handle(current()).invokeExact(this, depth+1, (int)res);  } catch (Throwable e) { rethrow(e); }}
 758             case MH_I_MANY, MH_C_MANY   -> {try { res += (int)handle(current()).invokeExact(this, depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4); } catch (Throwable e) { rethrow(e); }}
 759             case REF_I_INT,  REF_C_INT  -> {try { res += (int)method(current()).invoke(this, depth+1, (int)res); } catch (InvocationTargetException e) { rethrow(e.getCause()); } catch (IllegalAccessException e) { assert false; }}
 760             case REF_I_MANY, REF_C_MANY -> {try { res += (int)method(current()).invoke(this, depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4); } catch (InvocationTargetException e) { rethrow(e.getCause()); } catch (IllegalAccessException e) { assert false; }}
 761             default -> throw new AssertionError("Unknown op: " + current());
 762             }
 763         }
 764 
 765         return log(res);
 766     }
 767 
 768     @DontInline
 769     int com_int(final int depth, int x) {
 770         int res = x;
 771 
 772         int x1 = (int)res, x2 = (int)res, x3 = (int)res, x4 = (int)res;
 773         double d1 = (double)res, d2 = (double)res, d3 = (double)res, d4 = (double)res;
 774         long l1 = (long)res, l2 = (long)res, l3 = (long)res, l4 = (long)res;
 775         float f1 = (float)res, f2 = (float)res, f3 = (float)res, f4 = (float)res;
 776         Object o1 = res, o2 = res, o3 = res, o4 = res;
 777 
 778         for (int c = 1, index0 = index; c > 0; c--, maybeResetIndex(index0)) { // index0 is the index to which we return when we loop
 779             switch (next(c)) {
 780             case THROW -> throwException();
 781             case LOOP  -> { c += 2; index0 = index; }
 782             case YIELD -> { preYield(); boolean y = Continuation.yield(SCOPE); postYield(y); c++; }
 783             case DONE  -> { break; }
 784             case CALL_I_INT  -> res += int_int(depth+1, (int)res);
 785             case CALL_C_INT  -> res += com_int(depth+1, (int)res);
 786             case CALL_I_DBL  -> res += (int)int_dbl(depth+1, res);
 787             case CALL_C_DBL  -> res += (int)com_dbl(depth+1, res);
 788             case CALL_I_PIN  -> res += int_pin(depth+1, (int)res);
 789             case CALL_C_PIN  -> res += com_pin(depth+1, (int)res);
 790             case CALL_I_MANY -> res += int_mny(depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4);
 791             case CALL_C_MANY -> res += com_mny(depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4);
 792             case CALL_I_CTCH -> {try { res += int_int(depth+1, (int)res); } catch (FuzzException e) {}}
 793             case CALL_C_CTCH -> {try { res += com_int(depth+1, (int)res); } catch (FuzzException e) {}}
 794             case MH_I_INT, MH_C_INT     -> {try { res += (int)handle(current()).invokeExact(this, depth+1, (int)res);  } catch (Throwable e) { rethrow(e); }}
 795             case MH_I_MANY, MH_C_MANY   -> {try { res += (int)handle(current()).invokeExact(this, depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4); } catch (Throwable e) { rethrow(e); }}
 796             case REF_I_INT,  REF_C_INT  -> {try { res += (int)method(current()).invoke(this, depth+1, (int)res); } catch (InvocationTargetException e) { rethrow(e.getCause()); } catch (IllegalAccessException e) { assert false; }}
 797             case REF_I_MANY, REF_C_MANY -> {try { res += (int)method(current()).invoke(this, depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4); } catch (InvocationTargetException e) { rethrow(e.getCause()); } catch (IllegalAccessException e) { assert false; }}
 798             default -> throw new AssertionError("Unknown op: " + current());
 799             }
 800         }
 801 
 802         return log(res);
 803     }
 804 
 805     @DontInline
 806     double int_dbl(final int depth, double x) {
 807         double res = 3.0;
 808 
 809         int x1 = (int)res, x2 = (int)res, x3 = (int)res, x4 = (int)res;
 810         double d1 = (double)res, d2 = (double)res, d3 = (double)res, d4 = (double)res;
 811         long l1 = (long)res, l2 = (long)res, l3 = (long)res, l4 = (long)res;
 812         float f1 = (float)res, f2 = (float)res, f3 = (float)res, f4 = (float)res;
 813         Object o1 = res, o2 = res, o3 = res, o4 = res;
 814 
 815         for (int c = 1, index0 = index; c > 0; c--, maybeResetIndex(index0)) { // index0 is the index to which we return when we loop
 816             switch (next(c)) {
 817             case THROW -> throwException();
 818             case LOOP  -> { c += 2; index0 = index; }
 819             case YIELD -> { preYield(); boolean y = Continuation.yield(SCOPE); postYield(y); c++; }
 820             case DONE  -> { break; }
 821             case CALL_I_INT  -> res += int_int(depth+1, (int)res);
 822             case CALL_C_INT  -> res += com_int(depth+1, (int)res);
 823             case CALL_I_DBL  -> res += (int)int_dbl(depth+1, res);
 824             case CALL_C_DBL  -> res += (int)com_dbl(depth+1, res);
 825             case CALL_I_PIN  -> res += int_pin(depth+1, (int)res);
 826             case CALL_C_PIN  -> res += com_pin(depth+1, (int)res);
 827             case CALL_I_MANY -> res += int_mny(depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4);
 828             case CALL_C_MANY -> res += com_mny(depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4);
 829             case CALL_I_CTCH -> {try { res += int_int(depth+1, (int)res); } catch (FuzzException e) {}}
 830             case CALL_C_CTCH -> {try { res += com_int(depth+1, (int)res); } catch (FuzzException e) {}}
 831             case MH_I_INT, MH_C_INT     -> {try { res += (int)handle(current()).invokeExact(this, depth+1, (int)res);  } catch (Throwable e) { rethrow(e); }}
 832             case MH_I_MANY, MH_C_MANY   -> {try { res += (int)handle(current()).invokeExact(this, depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4); } catch (Throwable e) { rethrow(e); }}
 833             case REF_I_INT,  REF_C_INT  -> {try { res += (int)method(current()).invoke(this, depth+1, (int)res); } catch (InvocationTargetException e) { rethrow(e.getCause()); } catch (IllegalAccessException e) { assert false; }}
 834             case REF_I_MANY, REF_C_MANY -> {try { res += (int)method(current()).invoke(this, depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4); } catch (InvocationTargetException e) { rethrow(e.getCause()); } catch (IllegalAccessException e) { assert false; }}
 835             default -> throw new AssertionError("Unknown op: " + current());
 836             }
 837         }
 838 
 839         return log(res);
 840     }
 841 
 842     @DontInline
 843     double com_dbl(final int depth, double x) {
 844         double res = 3.0;
 845 
 846         int x1 = (int)res, x2 = (int)res, x3 = (int)res, x4 = (int)res;
 847         double d1 = (double)res, d2 = (double)res, d3 = (double)res, d4 = (double)res;
 848         long l1 = (long)res, l2 = (long)res, l3 = (long)res, l4 = (long)res;
 849         float f1 = (float)res, f2 = (float)res, f3 = (float)res, f4 = (float)res;
 850         Object o1 = res, o2 = res, o3 = res, o4 = res;
 851 
 852         for (int c = 1, index0 = index; c > 0; c--, maybeResetIndex(index0)) { // index0 is the index to which we return when we loop
 853             switch (next(c)) {
 854             case THROW -> throwException();
 855             case LOOP  -> { c += 2; index0 = index; }
 856             case YIELD -> { preYield(); boolean y = Continuation.yield(SCOPE); postYield(y); c++; }
 857             case DONE  -> { break; }
 858             case CALL_I_INT  -> res += int_int(depth+1, (int)res);
 859             case CALL_C_INT  -> res += com_int(depth+1, (int)res);
 860             case CALL_I_DBL  -> res += (int)int_dbl(depth+1, res);
 861             case CALL_C_DBL  -> res += (int)com_dbl(depth+1, res);
 862             case CALL_I_PIN  -> res += int_pin(depth+1, (int)res);
 863             case CALL_C_PIN  -> res += com_pin(depth+1, (int)res);
 864             case CALL_I_MANY -> res += int_mny(depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4);
 865             case CALL_C_MANY -> res += com_mny(depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4);
 866             case CALL_I_CTCH -> {try { res += int_int(depth+1, (int)res); } catch (FuzzException e) {}}
 867             case CALL_C_CTCH -> {try { res += com_int(depth+1, (int)res); } catch (FuzzException e) {}}
 868             case MH_I_INT, MH_C_INT     -> {try { res += (int)handle(current()).invokeExact(this, depth+1, (int)res);  } catch (Throwable e) { rethrow(e); }}
 869             case MH_I_MANY, MH_C_MANY   -> {try { res += (int)handle(current()).invokeExact(this, depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4); } catch (Throwable e) { rethrow(e); }}
 870             case REF_I_INT,  REF_C_INT  -> {try { res += (int)method(current()).invoke(this, depth+1, (int)res); } catch (InvocationTargetException e) { rethrow(e.getCause()); } catch (IllegalAccessException e) { assert false; }}
 871             case REF_I_MANY, REF_C_MANY -> {try { res += (int)method(current()).invoke(this, depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4); } catch (InvocationTargetException e) { rethrow(e.getCause()); } catch (IllegalAccessException e) { assert false; }}
 872             default -> throw new AssertionError("Unknown op: " + current());
 873             }
 874         }
 875 
 876         return log(res);
 877     }
 878 
 879     @DontInline
 880     int int_pin(final int depth, int x) {
 881         int res = x;
 882 
 883         int x1 = (int)res, x2 = (int)res, x3 = (int)res, x4 = (int)res;
 884         double d1 = (double)res, d2 = (double)res, d3 = (double)res, d4 = (double)res;
 885         long l1 = (long)res, l2 = (long)res, l3 = (long)res, l4 = (long)res;
 886         float f1 = (float)res, f2 = (float)res, f3 = (float)res, f4 = (float)res;
 887         Object o1 = res, o2 = res, o3 = res, o4 = res;
 888 
 889         synchronized (this) {
 890 
 891         for (int c = 1, index0 = index; c > 0; c--, maybeResetIndex(index0)) { // index0 is the index to which we return when we loop
 892             switch (next(c)) {
 893             case THROW -> throwException();
 894             case LOOP  -> { c += 2; index0 = index; }
 895             case YIELD -> { preYield(); boolean y = Continuation.yield(SCOPE); postYield(y); c++; }
 896             case DONE  -> { break; }
 897             case CALL_I_INT  -> res += int_int(depth+1, (int)res);
 898             case CALL_C_INT  -> res += com_int(depth+1, (int)res);
 899             case CALL_I_DBL  -> res += (int)int_dbl(depth+1, res);
 900             case CALL_C_DBL  -> res += (int)com_dbl(depth+1, res);
 901             case CALL_I_PIN  -> res += int_pin(depth+1, (int)res);
 902             case CALL_C_PIN  -> res += com_pin(depth+1, (int)res);
 903             case CALL_I_MANY -> res += int_mny(depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4);
 904             case CALL_C_MANY -> res += com_mny(depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4);
 905             case CALL_I_CTCH -> {try { res += int_int(depth+1, (int)res); } catch (FuzzException e) {}}
 906             case CALL_C_CTCH -> {try { res += com_int(depth+1, (int)res); } catch (FuzzException e) {}}
 907             case MH_I_INT, MH_C_INT     -> {try { res += (int)handle(current()).invokeExact(this, depth+1, (int)res);  } catch (Throwable e) { rethrow(e); }}
 908             case MH_I_MANY, MH_C_MANY   -> {try { res += (int)handle(current()).invokeExact(this, depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4); } catch (Throwable e) { rethrow(e); }}
 909             case REF_I_INT,  REF_C_INT  -> {try { res += (int)method(current()).invoke(this, depth+1, (int)res); } catch (InvocationTargetException e) { rethrow(e.getCause()); } catch (IllegalAccessException e) { assert false; }}
 910             case REF_I_MANY, REF_C_MANY -> {try { res += (int)method(current()).invoke(this, depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4); } catch (InvocationTargetException e) { rethrow(e.getCause()); } catch (IllegalAccessException e) { assert false; }}
 911             default -> throw new AssertionError("Unknown op: " + current());
 912             }
 913         }
 914 
 915         }
 916 
 917         return log(res);
 918     }
 919 
 920     @DontInline
 921     int com_pin(final int depth, int x) {
 922         int res = x;
 923 
 924         int x1 = (int)res, x2 = (int)res, x3 = (int)res, x4 = (int)res;
 925         double d1 = (double)res, d2 = (double)res, d3 = (double)res, d4 = (double)res;
 926         long l1 = (long)res, l2 = (long)res, l3 = (long)res, l4 = (long)res;
 927         float f1 = (float)res, f2 = (float)res, f3 = (float)res, f4 = (float)res;
 928         Object o1 = res, o2 = res, o3 = res, o4 = res;
 929 
 930         synchronized (this) {
 931 
 932         for (int c = 1, index0 = index; c > 0; c--, maybeResetIndex(index0)) { // index0 is the index to which we return when we loop
 933             switch (next(c)) {
 934             case THROW -> throwException();
 935             case LOOP  -> { c += 2; index0 = index; }
 936             case YIELD -> { preYield(); boolean y = Continuation.yield(SCOPE); postYield(y); c++; }
 937             case DONE  -> { break; }
 938             case CALL_I_INT  -> res += int_int(depth+1, (int)res);
 939             case CALL_C_INT  -> res += com_int(depth+1, (int)res);
 940             case CALL_I_DBL  -> res += (int)int_dbl(depth+1, res);
 941             case CALL_C_DBL  -> res += (int)com_dbl(depth+1, res);
 942             case CALL_I_PIN  -> res += int_pin(depth+1, (int)res);
 943             case CALL_C_PIN  -> res += com_pin(depth+1, (int)res);
 944             case CALL_I_MANY -> res += int_mny(depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4);
 945             case CALL_C_MANY -> res += com_mny(depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4);
 946             case CALL_I_CTCH -> {try { res += int_int(depth+1, (int)res); } catch (FuzzException e) {}}
 947             case CALL_C_CTCH -> {try { res += com_int(depth+1, (int)res); } catch (FuzzException e) {}}
 948             case MH_I_INT, MH_C_INT     -> {try { res += (int)handle(current()).invokeExact(this, depth+1, (int)res);  } catch (Throwable e) { rethrow(e); }}
 949             case MH_I_MANY, MH_C_MANY   -> {try { res += (int)handle(current()).invokeExact(this, depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4); } catch (Throwable e) { rethrow(e); }}
 950             case REF_I_INT,  REF_C_INT  -> {try { res += (int)method(current()).invoke(this, depth+1, (int)res); } catch (InvocationTargetException e) { rethrow(e.getCause()); } catch (IllegalAccessException e) { assert false; }}
 951             case REF_I_MANY, REF_C_MANY -> {try { res += (int)method(current()).invoke(this, depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4); } catch (InvocationTargetException e) { rethrow(e.getCause()); } catch (IllegalAccessException e) { assert false; }}
 952             default -> throw new AssertionError("Unknown op: " + current());
 953             }
 954         }
 955 
 956         }
 957 
 958         return log(res);
 959     }
 960 
 961     @DontInline
 962     int int_mny(int depth,
 963         int x1, double d1, long l1, float f1, Object o1,
 964         int x2, double d2, long l2, float f2, Object o2,
 965         int x3, double d3, long l3, float f3, Object o3,
 966         int x4, double d4, long l4, float f4, Object o4) {
 967 
 968         double res = x1 + d2 + f3 + l4 + (double)(o4 instanceof Double ? (Double)o4 : (Integer)o4);
 969 
 970         for (int c = 1, index0 = index; c > 0; c--, maybeResetIndex(index0)) { // index0 is the index to which we return when we loop
 971             switch (next(c)) {
 972             case THROW -> throwException();
 973             case LOOP  -> { c += 2; index0 = index; }
 974             case YIELD -> { preYield(); boolean y = Continuation.yield(SCOPE); postYield(y); c++; }
 975             case DONE  -> { break; }
 976             case CALL_I_INT  -> res += int_int(depth+1, (int)res);
 977             case CALL_C_INT  -> res += com_int(depth+1, (int)res);
 978             case CALL_I_DBL  -> res += (int)int_dbl(depth+1, res);
 979             case CALL_C_DBL  -> res += (int)com_dbl(depth+1, res);
 980             case CALL_I_PIN  -> res += int_pin(depth+1, (int)res);
 981             case CALL_C_PIN  -> res += com_pin(depth+1, (int)res);
 982             case CALL_I_MANY -> res += int_mny(depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4);
 983             case CALL_C_MANY -> res += com_mny(depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4);
 984             case CALL_I_CTCH -> {try { res += int_int(depth+1, (int)res); } catch (FuzzException e) {}}
 985             case CALL_C_CTCH -> {try { res += com_int(depth+1, (int)res); } catch (FuzzException e) {}}
 986             case MH_I_INT, MH_C_INT     -> {try { res += (int)handle(current()).invokeExact(this, depth+1, (int)res);  } catch (Throwable e) { rethrow(e); }}
 987             case MH_I_MANY, MH_C_MANY   -> {try { res += (int)handle(current()).invokeExact(this, depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4); } catch (Throwable e) { rethrow(e); }}
 988             case REF_I_INT,  REF_C_INT  -> {try { res += (int)method(current()).invoke(this, depth+1, (int)res); } catch (InvocationTargetException e) { rethrow(e.getCause()); } catch (IllegalAccessException e) { assert false; }}
 989             case REF_I_MANY, REF_C_MANY -> {try { res += (int)method(current()).invoke(this, depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4); } catch (InvocationTargetException e) { rethrow(e.getCause()); } catch (IllegalAccessException e) { assert false; }}
 990             default -> throw new AssertionError("Unknown op: " + current());
 991             }
 992         }
 993 
 994         return log((int)res);
 995     }
 996 
 997     @DontInline
 998     int com_mny(int depth,
 999         int x1, double d1, long l1, float f1, Object o1,
1000         int x2, double d2, long l2, float f2, Object o2,
1001         int x3, double d3, long l3, float f3, Object o3,
1002         int x4, double d4, long l4, float f4, Object o4) {
1003 
1004         double res = x1 + d2 + f3 + l4 + (double)(o4 instanceof Double ? (Double)o4 : (Integer)o4);
1005 
1006         for (int c = 1, index0 = index; c > 0; c--, maybeResetIndex(index0)) { // index0 is the index to which we return when we loop
1007             switch (next(c)) {
1008             case THROW -> throwException();
1009             case LOOP  -> { c += 2; index0 = index; }
1010             case YIELD -> { preYield(); boolean y = Continuation.yield(SCOPE); postYield(y); c++; }
1011             case DONE  -> { break; }
1012             case CALL_I_INT  -> res += int_int(depth+1, (int)res);
1013             case CALL_C_INT  -> res += com_int(depth+1, (int)res);
1014             case CALL_I_DBL  -> res += (int)int_dbl(depth+1, res);
1015             case CALL_C_DBL  -> res += (int)com_dbl(depth+1, res);
1016             case CALL_I_PIN  -> res += int_pin(depth+1, (int)res);
1017             case CALL_C_PIN  -> res += com_pin(depth+1, (int)res);
1018             case CALL_I_MANY -> res += int_mny(depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4);
1019             case CALL_C_MANY -> res += com_mny(depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4);
1020             case CALL_I_CTCH -> {try { res += int_int(depth+1, (int)res); } catch (FuzzException e) {}}
1021             case CALL_C_CTCH -> {try { res += com_int(depth+1, (int)res); } catch (FuzzException e) {}}
1022             case MH_I_INT, MH_C_INT     -> {try { res += (int)handle(current()).invokeExact(this, depth+1, (int)res);  } catch (Throwable e) { rethrow(e); }}
1023             case MH_I_MANY, MH_C_MANY   -> {try { res += (int)handle(current()).invokeExact(this, depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4); } catch (Throwable e) { rethrow(e); }}
1024             case REF_I_INT,  REF_C_INT  -> {try { res += (int)method(current()).invoke(this, depth+1, (int)res); } catch (InvocationTargetException e) { rethrow(e.getCause()); } catch (IllegalAccessException e) { assert false; }}
1025             case REF_I_MANY, REF_C_MANY -> {try { res += (int)method(current()).invoke(this, depth+1, x1, d1, l1, f1, o1, x2, d2, l2, f2, o2, x3, d3, l3, f3, o3, x4, d4, l4, f4, o4); } catch (InvocationTargetException e) { rethrow(e.getCause()); } catch (IllegalAccessException e) { assert false; }}
1026             default -> throw new AssertionError("Unknown op: " + current());
1027             }
1028         }
1029 
1030         return log((int)res);
1031     }
1032 }