public IRubyObject interpret(
      ThreadContext context,
      IRubyObject self,
      InterpreterContext interpreterContext,
      RubyModule implClass,
      String name,
      IRubyObject[] args,
      Block block,
      Block.Type blockType) {
    Instr[] instrs = interpreterContext.getInstructions();
    Object[] temp = interpreterContext.allocateTemporaryVariables();
    int n = instrs.length;
    int ipc = 0;
    Object exception = null;

    if (interpreterContext.receivesKeywordArguments())
      IRRuntimeHelpers.frobnicateKwargsArgument(
          context, interpreterContext.getRequiredArgsCount(), args);

    StaticScope currScope = interpreterContext.getStaticScope();
    DynamicScope currDynScope = context.getCurrentScope();
    boolean acceptsKeywordArgument = interpreterContext.receivesKeywordArguments();

    Stack<Integer> rescuePCs = null;

    // Init profiling this scope
    boolean debug = IRRuntimeHelpers.isDebug();
    boolean profile = IRRuntimeHelpers.inProfileMode();
    Integer scopeVersion = profile ? Profiler.initProfiling(interpreterContext.getScope()) : 0;

    // Enter the looooop!
    while (ipc < n) {
      Instr instr = instrs[ipc];

      Operation operation = instr.getOperation();
      if (debug) {
        Interpreter.LOG.info("I: {" + ipc + "} ", instr + "; <#RPCs=" + rescuePCs.size() + ">");
        Interpreter.interpInstrsCount++;
      } else if (profile) {
        Profiler.instrTick(operation);
        Interpreter.interpInstrsCount++;
      }

      ipc++;

      try {
        switch (operation.opClass) {
          case ARG_OP:
            receiveArg(
                context,
                instr,
                operation,
                args,
                acceptsKeywordArgument,
                currDynScope,
                temp,
                exception,
                block);
            break;
          case CALL_OP:
            if (profile)
              Profiler.updateCallSite(instr, interpreterContext.getScope(), scopeVersion);
            processCall(context, instr, operation, currDynScope, currScope, temp, self);
            break;
          case RET_OP:
            return processReturnOp(
                context, instr, operation, currDynScope, temp, self, blockType, currScope);
          case BRANCH_OP:
            switch (operation) {
              case JUMP:
                JumpInstr jump = ((JumpInstr) instr);
                if (jump.exitsExcRegion()) {
                  rescuePCs.pop();
                }
                ipc = jump.getJumpTarget().getTargetPC();
                break;
              default:
                ipc =
                    instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc);
                break;
            }
            break;
          case BOOK_KEEPING_OP:
            switch (operation) {
              case PUSH_BINDING:
                // IMPORTANT: Preserve this update of currDynScope.
                // This affects execution of all instructions in this scope
                // which will now use the updated value of currDynScope.
                currDynScope = interpreterContext.newDynamicScope(context);
                context.pushScope(currDynScope);
              case EXC_REGION_START:
                if (rescuePCs == null) rescuePCs = new Stack<>();
                rescuePCs.push(
                    ((ExceptionRegionStartMarkerInstr) instr)
                        .getFirstRescueBlockLabel()
                        .getTargetPC());
                break;
              case EXC_REGION_END:
                rescuePCs.pop();
                break;
              default:
                processBookKeepingOp(
                    context, instr, operation, name, args, self, block, blockType, implClass);
            }
            break;
          case OTHER_OP:
            processOtherOp(
                context, instr, operation, currDynScope, currScope, temp, self, blockType);
            break;
        }
      } catch (Throwable t) {
        if (debug) extractToMethodToAvoidC2Crash(instr, t);

        if (rescuePCs == null
            || rescuePCs.empty()
            || (t instanceof IRBreakJump && instr instanceof BreakInstr)
            || (t instanceof IRReturnJump && instr instanceof NonlocalReturnInstr)) {
          ipc = -1;
        } else {
          ipc = rescuePCs.pop();
        }

        if (debug) {
          Interpreter.LOG.info(
              "in : "
                  + interpreterContext.getScope()
                  + ", caught Java throwable: "
                  + t
                  + "; excepting instr: "
                  + instr);
          Interpreter.LOG.info("ipc for rescuer: " + ipc);
        }

        if (ipc == -1) {
          Helpers.throwException(t);
        } else {
          exception = t;
        }
      }
    }

    // Control should never get here!
    throw context.runtime.newRuntimeError("BUG: interpreter fell through to end unexpectedly");
  }