/*
    SuperCollider real time audio synthesis system
    Copyright (c) 2002 James McCartney. All rights reserved.
    http://www.audiosynth.com

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
*/


#include "Opcodes.h"
#include "PyrInterpreter.h"
#include "PyrPrimitive.h"
#include "PyrPrimitiveProto.h"
#include "PyrMathPrim.h"
#include "PyrListPrim.h"
#include "PyrKernel.h"
#include "PyrMessage.h"
#include "PyrParseNode.h"
#include "PyrSignal.h"
#include "PyrSched.h"
#include "SC_InlineUnaryOp.h"
#include "SC_InlineBinaryOp.h"
#include "PyrKernelProto.h"
#include "PyrSymbolTable.h"
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>

#include <boost/chrono.hpp>

#include <float.h>
#define kBigBigFloat DBL_MAX
#define kSmallSmallFloat DBL_MIN


#include <new>
#include "InitAlloc.h"
#include "function_attributes.h"

#ifdef __clang__
#    pragma clang diagnostic ignored "-Warray-bounds"
#endif


// void tellPlugInsAboutToRun();
double timeNow();


int32 timeseed() {
    using namespace std::chrono;

    high_resolution_clock::time_point now = high_resolution_clock::now();
    high_resolution_clock::duration since_epoch = now.time_since_epoch();

    seconds secs = duration_cast<seconds>(since_epoch);
    nanoseconds nanosecs = since_epoch - secs;

    int_least64_t seed = secs.count() ^ nanosecs.count();

    return (int32)seed;
}

VMGlobals gVMGlobals;
VMGlobals* gMainVMGlobals = &gVMGlobals;

void debugf(char* fmt, ...);

#define DEBUGINTERPRETER 0
#define METHODMETER 0
#define BCSTAT 0
#define CHECK_MAX_STACK_USE 0


#if CHECK_MAX_STACK_USE
int gMaxStackDepth = 0;
#endif

unsigned char* dumpOneByteCode(PyrBlock* theBlock, PyrClass* theClass, unsigned char* ip);
void dumpSlotOneWord(const char* tagstr, PyrSlot* slot);
// bool checkAllObjChecksum(PyrObject* obj);

bool gTraceInterpreter = false;
// bool gTraceInterpreter = true;


const char* byteCodeString(int code);

extern int gNumClasses;
extern PyrClass* gClassList;

// runInterpreter has 7 call sites:
//	compileLibrary (4)
// 	runLibrary
// 	interpretCmdLine

static void endInterpreter(VMGlobals* g);


SCLANG_DLLEXPORT_C void runInterpreter(VMGlobals* g, PyrSymbol* selector, int numArgsPushed) {
    // postfl("->runInterpreter\n");
#ifdef GC_SANITYCHECK
    g->gc->SanityCheck();
#endif
    // postfl(" >initInterpreter\n");

    if (initInterpreter(g, selector, numArgsPushed)) {
#ifdef GC_SANITYCHECK
        g->gc->SanityCheck();
#endif
        //        if (strcmp(selector->name, "tick") != 0) post("%s %d  execMethod %d\n", selector->name, numArgsPushed,
        //        g->execMethod);
        // post("->Interpret thread %p\n", g->thread);
        if (g->execMethod)
            Interpret(g);
            // post("<-Interpret thread %p\n", g->thread);
#ifdef GC_SANITYCHECK
        g->gc->SanityCheck();
#endif
    }

    // postfl(" >endInterpreter\n");
    endInterpreter(g);
#ifdef GC_SANITYCHECK
    g->gc->SanityCheck();
#endif
    // postfl("<-runInterpreter\n");
    // dumpGCStats(g->gc);
}

static bool initAwakeMessage(VMGlobals* g);

void runAwakeMessage(VMGlobals* g) {
    if (initAwakeMessage(g)) {
        if (g->execMethod)
            Interpret(g);
    }
    endInterpreter(g);
}

void initPyrThread(VMGlobals* g, PyrThread* thread, PyrSlot* func, int stacksize, PyrInt32Array* rgenArray,
                   double beats, double seconds, PyrSlot* clock, bool runGC);
int32 timeseed();

PyrProcess* newPyrProcess(VMGlobals* g, PyrClass* procclassobj) {
    PyrGC* gc = g->gc;
    PyrProcess* proc = (PyrProcess*)instantiateObject(gc, procclassobj, 0, true, false);

    PyrObject* sysSchedulerQueue = newPyrArray(gc, 4096, 0, false);
    sysSchedulerQueue->size = 1;
    SetInt(sysSchedulerQueue->slots + 0, 0); // stability count
    SetObject(&proc->sysSchedulerQueue, sysSchedulerQueue);

    PyrObject* classVars = newPyrArray(gc, gNumClassVars, 0, false);
    classVars->size = gNumClassVars;
    nilSlots(classVars->slots, gNumClassVars);
    SetObject(&proc->classVars, classVars);
    g->classvars = classVars;

    // fill class vars from prototypes:
    PyrClass* classobj = gClassList;
    while (classobj) {
        if (IsObj(&classobj->cprototype)) {
            int numClassVars = slotRawObject(&classobj->cprototype)->size;
            if (numClassVars > 0) {
                memcpy(g->classvars->slots + slotRawInt(&classobj->classVarIndex),
                       slotRawObject(&classobj->cprototype)->slots, numClassVars * sizeof(PyrSlot));
            }
        }
        classobj = slotRawClass(&classobj->nextclass);
    }

    SetNil(&proc->nowExecutingPath);

    class_thread = getsym("Thread")->u.classobj;
    if (class_thread) {
        SetNil(&proc->curThread);
        PyrThread* thread = (PyrThread*)instantiateObject(gc, class_thread, 0, true, false);
        // SetObject(&threadsArray->slots[0], thread);
        SetObject(&proc->mainThread, thread);
        PyrInt32Array* rgenArray = newPyrInt32Array(gc, 4, 0, false);
        rgenArray->size = 4;
        rgenArray->i[0] = 0;
        rgenArray->i[1] = 0;
        rgenArray->i[2] = 0;
        rgenArray->i[3] = 0;
        ((RGen*)(rgenArray->i))->init(timeseed());

        PyrSlot clockSlot;
        SetObject(&clockSlot, s_systemclock->u.classobj);
        initPyrThread(g, thread, &o_nil, EVALSTACKDEPTH, rgenArray, 0., 0., &clockSlot, false);
        // postfl("elapsedTime %.6f\n", elapsedTime());
    } else {
        error("Class Thread not found.\n");
    }

    PyrSymbol* contextsym = getsym("functionCompileContext");
    size_t index = slotRawInt(&class_interpreter->classIndex) + contextsym->u.index;
    PyrMethod* meth = gRowTable[index];
    if (!meth || slotRawSymbol(&meth->name) != contextsym) {
        error("compile context method 'functionCompileContext' not found.\n");
        // SetNil(&proc->interpreter);
    } else {
        PyrObject* proto;
        PyrFrame* frame;
        PyrMethodRaw* methraw;

        PyrInterpreter* interpreter = (PyrInterpreter*)instantiateObject(gc, class_interpreter, 0, true, false);
        SetObject(&proc->interpreter, interpreter);
        proto = slotRawObject(&meth->prototypeFrame);

        methraw = METHRAW(meth);
        frame = (PyrFrame*)gc->New(methraw->frameSize, 0, obj_slot, false);
        frame->classptr = class_frame;
        frame->size = FRAMESIZE + proto->size; /// <- IS THIS WRONG ??
        SetObject(&frame->method, meth);
        SetObject(&frame->homeContext, frame);
        SetInt(&frame->caller, 0);
        SetNil(&frame->context);
        SetPtr(&frame->ip, 0);
        SetObject(&frame->vars[0], interpreter);

        SetObject(&interpreter->context, frame);
    }

    return proc;
}


#if BCSTAT
int prevop = 0;
int bcstat[256];
int bcpair[256][256];

void clearbcstat();
void clearbcstat() {
    int i, j;
    for (i = 0; i < 256; ++i) {
        bcstat[i] = 0;
        for (j = 0; j < 256; ++j) {
            bcpair[i][j] = 0;
        }
    }
}

void dumpbcstat();
void dumpbcstat() {
    FILE* file;
    int i, j, k, total;

    file = fopen("bcstat", "w");
    if (file) {
        fprintf(file, "----------\n");
        total = 0;
        for (i = 0; i < 256; ++i) {
            total += bcstat[i];
            if (bcstat[i])
                fprintf(file, "%3d %8d  %-32s\n", i, bcstat[i], byteCodeString(i));
        }
        fprintf(file, "\ntotal %d\n", total);
        fprintf(file, "-----cur,next-----\n");
        for (i = 0, k = 0; i < 256; ++i) {
            for (j = 0; j < 256; j++) {
                if (bcpair[i][j] > 0) {
                    fprintf(file, "%4d %3d %3d %8d %-32s %-32s\n", k++, i, j, bcpair[i][j], byteCodeString(i),
                            byteCodeString(j));
                }
            }
        }
        fprintf(file, "-----cur,prev-----\n");
        for (i = 0, k = 0; i < 256; ++i) {
            for (j = 0; j < 256; j++) {
                if (bcpair[j][i] > 0) {
                    fprintf(file, "%4d %3d %3d %8d %-32s %-32s\n", k++, i, j, bcpair[j][i], byteCodeString(i),
                            byteCodeString(j));
                }
            }
        }
    }
    fclose(file);
}
#endif

void initPatterns();
void initThreads();
void initGUI();

#ifndef _WIN32
bool running = true;
static void handleSigUsr1(int param) {
    printf("handleSigUsr1()...\n");
    running = false;
}
#endif

bool initRuntime(VMGlobals* g, int poolSize, AllocPool* inPool) {
    /*
        create a GC environment
        create a vmachine instance of main
        initialize VMGlobals
    */

    PyrClass* class_main = s_main->u.classobj;

    if (!class_main) {
        error("Class 'Main' not defined.\n");
        return false;
    }
    if (!isSubclassOf(class_main, class_process)) {
        error("Class Main is not a subclass of Process.\n");
        return false;
    }

    // create GC environment, process
    g->allocPool = inPool;
    g->gc = (PyrGC*)g->allocPool->Alloc(sizeof(PyrGC));
    new (g->gc) PyrGC(g, g->allocPool, class_main, poolSize);
    g->thread = slotRawThread(&g->process->mainThread);
    SetObject(&g->receiver, g->process);

    // these will be set up when the run method is called
    g->method = NULL;
    g->block = NULL;
    g->frame = NULL;
    g->ip = NULL;

    // initialize process random number generator
    g->rgen = (RGen*)(slotRawObject(&g->thread->randData)->slots);

    initThreads();
    initPatterns();
    initUniqueMethods();
    initGUI();
#ifdef GC_SANITYCHECK
    g->gc->SanityCheck();
#endif
    // tellPlugInsAboutToRun();

#ifndef _WIN32
    signal(SIGUSR1, handleSigUsr1);
#endif

    assert((g->gc->SanityCheck()));
#ifdef GC_SANITYCHECK
    g->gc->SanityCheck();
#endif

    return true;
}

static bool initAwakeMessage(VMGlobals* g) {
    // post("initAwakeMessage %p %p\n", g->thread, slotRawThread(&g->process->mainThread));
    slotCopy(&g->process->curThread, &g->process->mainThread); //??
    g->thread = slotRawThread(&g->process->mainThread); //??

    // these will be set up when the run method is called
    g->method = NULL;
    g->block = NULL;
    g->frame = NULL;
    g->ip = NULL;
    g->execMethod = 0;

    // set process as the receiver
    PyrSlot* slot = g->sp - 3;
    slotCopy(&g->receiver, slot);

    SetFloat(&g->thread->beats, slotRawFloat(&slot[1]));
    SetFloat(&g->thread->seconds, slotRawFloat(&slot[2]));
    slotCopy(&g->thread->clock, &slot[3]);
    g->gc->GCWrite(g->thread, slot + 3);

    // start it
    sendMessage(g, s_awake, 4);

    return g->method != NULL;
}

bool initInterpreter(VMGlobals* g, PyrSymbol* selector, int numArgsPushed) {
    slotCopy(&g->process->curThread, &g->process->mainThread);
    g->thread = slotRawThread(&g->process->mainThread);

    // these will be set up when the run method is called
#if TAILCALLOPTIMIZE
    g->tailCall = 0;
#endif
    g->method = NULL;
    g->block = NULL;
    g->frame = NULL;
    g->ip = NULL;
    g->execMethod = 0;
    double elapsed = elapsedTime();
    SetFloat(&g->thread->beats, elapsed);
    SetFloat(&g->thread->seconds, elapsed);
    SetObject(&g->thread->clock, s_systemclock->u.classobj);
    g->gc->GCWrite(g->thread, s_systemclock->u.classobj);

    // set process as the receiver
    PyrSlot* slot = g->sp - numArgsPushed + 1;
    slotCopy(&g->receiver, slot);

    // start it
    sendMessage(g, selector, numArgsPushed);

    return g->method != NULL;
}


static void endInterpreter(VMGlobals* g) {
    slotCopy(&g->result, g->sp);
    //	dumpObjectSlot(&g->result);
    g->gc->Stack()->size = 0;
    g->sp = g->gc->Stack()->slots - 1;
    g->gc->LazyCollect();
}


static void StoreToImmutableA(VMGlobals* g, PyrSlot*& sp, unsigned char*& ip) {
    // only the value is on the stack
    slotCopy(sp + 1, sp); // copy value up one
    slotCopy(sp, &g->receiver); // put receiver in place
    sp++;
    g->sp = sp;
    g->ip = ip;
    post("StoreToImmutableA\n");
    dumpObjectSlot(sp - 1);
    dumpObjectSlot(sp);
    sendMessage(g, getsym("immutableError"), 2);
    sp = g->sp;
    ip = g->ip;
}

void StoreToImmutableB(VMGlobals* g, PyrSlot*& sp, unsigned char*& ip) {
    // receiver and value are on the stack.
    sp++;
    g->sp = sp;
    g->ip = ip;
    post("StoreToImmutableB\n");
    dumpObjectSlot(sp - 1);
    dumpObjectSlot(sp);
    PyrSymbol* selector = getsym("immutableError");
    sendMessage(g, selector, 2);
    sp = g->sp;
    ip = g->ip;
}


void dumpByteCodes(PyrBlock* theBlock);

static inline void handlePushClassVar(VMGlobals* g, PyrSlot*& sp, unsigned char*& ip, unsigned char op2) {
    unsigned char op3 = ip[1];
    ++ip; // get class var index
    slotCopy(++sp, &g->classvars->slots[(op2 << 8) | op3]);
}

static inline void handleStoreInstVar(VMGlobals* g, PyrSlot*& sp, unsigned char*& ip, unsigned int index) {
    PyrObject* obj = slotRawObject(&g->receiver);
    if (obj->IsImmutable())
        StoreToImmutableA(g, sp, ip);
    else {
        PyrSlot* slot = obj->slots + index;
        slotCopy(slot, sp--);
        g->gc->GCWrite(obj, slot);
    }
}

static inline void handleSendSpecialUnaryArithMsg(VMGlobals* g, PyrSlot*& sp, unsigned char*& ip, unsigned char op1) {
    g->sp = sp;
    g->ip = ip;
    g->primitiveIndex = op1 & 15;
    doSpecialUnaryArithMsg(g, -1);
    sp = g->sp;
    ip = g->ip;
}

static inline void handleSendSpecialBinaryArithMsg(VMGlobals* g, PyrSlot*& sp, unsigned char*& ip, unsigned char op1) {
    g->sp = sp;
    g->ip = ip;
    g->primitiveIndex = op1 & 15;
    doSpecialBinaryArithMsg(g, 2, false);
    sp = g->sp;
    ip = g->ip;
}

static inline bool checkStackOverflow(VMGlobals* g, PyrSlot* sp) {
    PyrObject* stack = g->gc->Stack();
    int depth = sp - stack->slots;
    return depth < slotRawInt(&g->thread->stackSize);
}

static inline void checkStackDepth(VMGlobals* g, PyrSlot* sp) {
#if CHECK_MAX_STACK_USE
    int stackDepth = sp - g->sp + 1;
    if (stackDepth > gMaxStackDepth) {
        gMaxStackDepth = stackDepth;
        printf("gMaxStackDepth %d\n", gMaxStackDepth);
    }
#endif
}

#if defined(__GNUC__) || defined(__INTEL_COMPILER)
#    define LABELS_AS_VALUES
#endif

#ifdef LABELS_AS_VALUES
#    define dispatch_opcode                                                                                            \
        op1 = ip[1];                                                                                                   \
        ++ip;                                                                                                          \
        checkStackDepth(g, sp);                                                                                        \
        assert(checkStackOverflow(g, sp));                                                                             \
        goto* opcode_labels[op1]
#else
#    define dispatch_opcode break
#endif

#if defined(__GNUC__)
#    define LIKELY(x) __builtin_expect((x), 1)
#    define UNLIKELY(x) __builtin_expect((x), 0)
#else
#    define LIKELY(x) x
#    define UNLIKELY(x) x
#endif

#if defined(__GNUC__) && !defined(__clang__)
// gcc manual:
// Note: When compiling a program using computed gotos, a GCC extension, you may get better run-time performance if
//       you disable the global common subexpression elimination pass by adding -fno-gcse to the command line.
#    pragma GCC push_options
#    pragma GCC optimize("-fno-gcse")
#endif

HOT void Interpret(VMGlobals* g) {
    // byte code values
    unsigned char* ip;
    unsigned char op1;

    // interpreter globals
    int numArgsPushed, numKeyArgsPushed;
    PyrSymbol* selector;
    PyrClass* classobj;

    // temporary variables used in the interpreter
    PyrSlot* slot;
    PyrSlot* sp;

#ifdef LABELS_AS_VALUES
    static void* opcode_labels[] = {
        &&handle_op_0,   &&handle_op_1,   &&handle_op_2,   &&handle_op_3,   &&handle_op_4,   &&handle_op_5,
        &&handle_op_6,   &&handle_op_7,   &&handle_op_8,   &&handle_op_9,   &&handle_op_10,  &&handle_op_11,
        &&handle_op_12,  &&handle_op_13,  &&handle_op_14,  &&handle_op_15,  &&handle_op_16,  &&handle_op_17,
        &&handle_op_18,  &&handle_op_19,  &&handle_op_20,  &&handle_op_21,  &&handle_op_22,  &&handle_op_23,
        &&handle_op_24,  &&handle_op_25,  &&handle_op_26,  &&handle_op_27,  &&handle_op_28,  &&handle_op_29,
        &&handle_op_30,  &&handle_op_31,  &&handle_op_32,  &&handle_op_33,  &&handle_op_34,  &&handle_op_35,
        &&handle_op_36,  &&handle_op_37,  &&handle_op_38,  &&handle_op_39,  &&handle_op_40,  &&handle_op_41,
        &&handle_op_42,  &&handle_op_43,  &&handle_op_44,  &&handle_op_45,  &&handle_op_46,  &&handle_op_47,
        &&handle_op_48,  &&handle_op_49,  &&handle_op_50,  &&handle_op_51,  &&handle_op_52,  &&handle_op_53,
        &&handle_op_54,  &&handle_op_55,  &&handle_op_56,  &&handle_op_57,  &&handle_op_58,  &&handle_op_59,
        &&handle_op_60,  &&handle_op_61,  &&handle_op_62,  &&handle_op_63,  &&handle_op_64,  &&handle_op_65,
        &&handle_op_66,  &&handle_op_67,  &&handle_op_68,  &&handle_op_69,  &&handle_op_70,  &&handle_op_71,
        &&handle_op_72,  &&handle_op_73,  &&handle_op_74,  &&handle_op_75,  &&handle_op_76,  &&handle_op_77,
        &&handle_op_78,  &&handle_op_79,  &&handle_op_80,  &&handle_op_81,  &&handle_op_82,  &&handle_op_83,
        &&handle_op_84,  &&handle_op_85,  &&handle_op_86,  &&handle_op_87,  &&handle_op_88,  &&handle_op_89,
        &&handle_op_90,  &&handle_op_91,  &&handle_op_92,  &&handle_op_93,  &&handle_op_94,  &&handle_op_95,
        &&handle_op_96,  &&handle_op_97,  &&handle_op_98,  &&handle_op_99,  &&handle_op_100, &&handle_op_101,
        &&handle_op_102, &&handle_op_103, &&handle_op_104, &&handle_op_105, &&handle_op_106, &&handle_op_107,
        &&handle_op_108, &&handle_op_109, &&handle_op_110, &&handle_op_111, &&handle_op_112, &&handle_op_113,
        &&handle_op_114, &&handle_op_115, &&handle_op_116, &&handle_op_117, &&handle_op_118, &&handle_op_119,
        &&handle_op_120, &&handle_op_121, &&handle_op_122, &&handle_op_123, &&handle_op_124, &&handle_op_125,
        &&handle_op_126, &&handle_op_127, &&handle_op_128, &&handle_op_129, &&handle_op_130, &&handle_op_131,
        &&handle_op_132, &&handle_op_133, &&handle_op_134, &&handle_op_135, &&handle_op_136, &&handle_op_137,
        &&handle_op_138, &&handle_op_139, &&handle_op_140, &&handle_op_141, &&handle_op_142, &&handle_op_143,
        &&handle_op_144, &&handle_op_145, &&handle_op_146, &&handle_op_147, &&handle_op_148, &&handle_op_149,
        &&handle_op_150, &&handle_op_151, &&handle_op_152, &&handle_op_153, &&handle_op_154, &&handle_op_155,
        &&handle_op_156, &&handle_op_157, &&handle_op_158, &&handle_op_159, &&handle_op_160, &&handle_op_161,
        &&handle_op_162, &&handle_op_163, &&handle_op_164, &&handle_op_165, &&handle_op_166, &&handle_op_167,
        &&handle_op_168, &&handle_op_169, &&handle_op_170, &&handle_op_171, &&handle_op_172, &&handle_op_173,
        &&handle_op_174, &&handle_op_175, &&handle_op_176, &&handle_op_177, &&handle_op_178, &&handle_op_179,
        &&handle_op_180, &&handle_op_181, &&handle_op_182, &&handle_op_183, &&handle_op_184, &&handle_op_185,
        &&handle_op_186, &&handle_op_187, &&handle_op_188, &&handle_op_189, &&handle_op_190, &&handle_op_191,
        &&handle_op_192, &&handle_op_193, &&handle_op_194, &&handle_op_195, &&handle_op_196, &&handle_op_197,
        &&handle_op_198, &&handle_op_199, &&handle_op_200, &&handle_op_201, &&handle_op_202, &&handle_op_203,
        &&handle_op_204, &&handle_op_205, &&handle_op_206, &&handle_op_207, &&handle_op_208, &&handle_op_209,
        &&handle_op_210, &&handle_op_211, &&handle_op_212, &&handle_op_213, &&handle_op_214, &&handle_op_215,
        &&handle_op_216, &&handle_op_217, &&handle_op_218, &&handle_op_219, &&handle_op_220, &&handle_op_221,
        &&handle_op_222, &&handle_op_223, &&handle_op_224, &&handle_op_225, &&handle_op_226, &&handle_op_227,
        &&handle_op_228, &&handle_op_229, &&handle_op_230, &&handle_op_231, &&handle_op_232, &&handle_op_233,
        &&handle_op_234, &&handle_op_235, &&handle_op_236, &&handle_op_237, &&handle_op_238, &&handle_op_239,
        &&handle_op_240, &&handle_op_241, &&handle_op_242, &&handle_op_243, &&handle_op_244, &&handle_op_245,
        &&handle_op_246, &&handle_op_247, &&handle_op_248, &&handle_op_249, &&handle_op_250, &&handle_op_251,
        &&handle_op_252, &&handle_op_253, &&handle_op_254, &&handle_op_255
    };

#endif

#if 0
	unsigned char *bzero;
	PyrSlot *szero;
	char str[80];
#endif


#if BCSTAT
    // clearbcstat();
    op1 = 0;
    prevop = 0;
#endif

#if 0
	bzero = g->ip;
	szero = g->sp;
	//SetSymbol(g->sp, getsym("STACK TOP")); // just for debugging
	//g->sp++; // just for debugging
#endif

    // Codewarrior puts them in registers. take advantage..
    sp = g->sp;
    ip = g->ip;

    numKeyArgsPushed = 0;

    if (setjmp(g->escapeInterpreter) != 0) {
        return;
    }
#ifndef _WIN32
    while (running) { // not going to indent body to save line space
#else
    while (true) {
#endif

        checkStackDepth(g, sp);

#if BCSTAT
        prevop = op1;
#endif

        op1 = ip[1];
        ++ip;

        assert(checkStackOverflow(g, sp));

#if BCSTAT
        ++bcstat[op1];
        ++bcpair[prevop][op1];
        prevop = op1;
#endif
        // printf("op1 %d\n", op1);
        // postfl("sp %p   frame %p  caller %p  ip %p\n", sp, g->frame, g->frame->caller.uof,
        // slotRawInt(&g->frame->caller.uof->ip)); postfl("sp %p   frame %p   diff %d    caller %p\n", sp, g->frame,
        // ((int)sp - (int)g->frame)>>3, g->frame->caller.uof);
#if DEBUGINTERPRETER
        if (gTraceInterpreter) {
            // DumpStack(g, sp);
            if (FrameSanity(g->frame, "dbgint")) {
                // Debugger();
            }
            // g->gc->SanityCheck();
            // assert(g->gc->SanityCheck());
            g->sp = sp;
            g->ip = ip;
            g->gc->FullCollection();
            sp = g->sp;
            ip = g->ip;
            postfl("[%3d] %20s-%-16s  ", (sp - g->gc->Stack()->slots) + 1,
                   slotRawSymbol(&slotRawClass(&g->method->ownerclass)->name)->name,
                   slotRawSymbol(&g->method->name)->name);
            dumpOneByteCode(g->block, NULL, ip);
        }
#endif
#ifdef GC_SANITYCHECK
        //	gcLinkSanity(g->gc);
        g->gc->SanityCheck();
//	do_check_pool(pyr_pool_runtime);
//	do_check_pool(pyr_pool_compile);
#endif
#if METHODMETER
        if (gTraceInterpreter) {
            slotRawInt(&g->method->byteMeter)++;
        }
#endif

        switch (op1) {
        case 0: //	push class
        handle_op_0 : {
            int op2 = ip[1];
            ++ip; // get literal index
            classobj = slotRawSymbol(&slotRawObject(&g->block->selectors)->slots[op2])->u.classobj;
            if (classobj) {
                ++sp;
                SetObject(sp, classobj);
            } else {
                postfl("Execution warning: Class '%s' not found\n",
                       slotRawSymbol(&slotRawObject(&g->block->selectors)->slots[op2])->name);
                slotCopy(++sp, &gSpecialValues[svNil]);
            }
            dispatch_opcode;
        }
        case 1: // opExtended, opPushInstVar
        handle_op_1 : {
            int op2 = ip[1];
            ++ip; // get inst var index
            slotCopy(++sp, &slotRawObject(&g->receiver)->slots[op2]);
            dispatch_opcode;
        }
        case 2: // opExtended, opPushTempVar
        handle_op_2 : {
            int op2 = ip[1]; // get temp var level
            int op3 = ip[2]; // get temp var index
            ip += 2;
            PyrFrame* tframe = g->frame;
            for (; op2--; tframe = slotRawFrame(&tframe->context)) { /* noop */
            }
            slotCopy(++sp, &tframe->vars[op3]);
            dispatch_opcode;
        }
        case 3: // opExtended, opPushTempZeroVar
        handle_op_3 : {
            int op2 = ip[1];
            ++ip; // get temp var index
            slotCopy(++sp, &g->frame->vars[op2]);
            dispatch_opcode;
        }
        case 4: // opExtended, opPushLiteral
        handle_op_4 : {
            int op2 = ip[1];
            ++ip; // get literal index
            // push a block as a closure if it is one
            slot = slotRawObject(&g->block->selectors)->slots + op2;
            if (IsObj(slot) && slotRawObject(slot)->classptr == gSpecialClasses[op_class_fundef]->u.classobj) {
                // push a closure
                g->sp = sp; // gc may push the stack
                PyrClosure* closure = (PyrClosure*)g->gc->New(2 * sizeof(PyrSlot), 0, obj_notindexed, true);
                sp = g->sp;
                closure->classptr = gSpecialClasses[op_class_func]->u.classobj;
                closure->size = 2;
                slotCopy(&closure->block, slot);
                if (IsNil(&slotRawBlock(slot)->contextDef)) {
                    slotCopy(&closure->context, &slotRawInterpreter(&g->process->interpreter)->context);
                } else {
                    SetObject(&closure->context, g->frame);
                }
                ++sp;
                SetObject(sp, closure);
            } else {
                slotCopy(++sp, slot);
            }
            dispatch_opcode;
        }
        case 5: // opExtended, opPushClassVar
        handle_op_5 : {
            int op2 = ip[1]; // get class
            int op3 = ip[2]; // get class var index
            ip += 2;
            slotCopy(++sp, &g->classvars->slots[(op2 << 8) | op3]);
            dispatch_opcode;
        }
        case 6: // opExtended, opPushSpecialValue == push a special class
        handle_op_6 : {
            int op2 = ip[1];
            ++ip; // get class name index
            classobj = gSpecialClasses[op2]->u.classobj;
            if (classobj) {
                ++sp;
                SetObject(sp, classobj);
            } else {
                slotCopy(++sp, &gSpecialValues[svNil]);
            }
            dispatch_opcode;
        }
        case 7: // opExtended, opStoreInstVar
        handle_op_7 : {
            int op2 = ip[1];
            ++ip; // get inst var index
            PyrObject* obj = slotRawObject(&g->receiver);
            if (obj->IsImmutable()) {
                StoreToImmutableA(g, sp, ip);
            } else {
                slot = obj->slots + op2;
                slotCopy(slot, sp);
                g->gc->GCWrite(obj, slot);
            }
            dispatch_opcode;
        }
        case 8: // opExtended, opStoreTempVar
        handle_op_8 : {
            int op2 = ip[1]; // get temp var level
            int op3 = ip[2]; // get temp var index
            ip += 2;
            PyrFrame* tframe = g->frame;
            for (; op2--; tframe = slotRawFrame(&tframe->context)) { /* noop */
            }
            slot = tframe->vars + op3;
            slotCopy(slot, sp);
            g->gc->GCWrite(tframe, slot);
            dispatch_opcode;
        }
        case 9: // opExtended, opStoreClassVar
        handle_op_9 : {
            int op2 = ip[1]; // get index of class name literal
            int op3 = ip[2]; // get class var index
            ip += 2;
            slotCopy(&g->classvars->slots[(op2 << 8) | op3], sp);
            g->gc->GCWrite(g->classvars, sp);
            dispatch_opcode;
        }
        case 10: // opExtended, opSendMsg
        handle_op_10 : {
            numArgsPushed = ip[1]; // get num args
            numKeyArgsPushed = ip[2]; // get num keyword args
            int op3 = ip[3]; // get selector index
            ip += 3;
            selector = slotRawSymbol(&slotRawObject(&g->block->selectors)->slots[op3]);

            slot = sp - numArgsPushed + 1;

            if (numKeyArgsPushed)
                goto key_class_lookup;
            else
                goto class_lookup;
        }
        case 11: // opExtended, opSendSuper
        handle_op_11 : {
            numArgsPushed = ip[1]; // get num args
            numKeyArgsPushed = ip[2]; // get num keyword args
            int op3 = ip[3]; // get selector index
            ip += 3;
            selector = slotRawSymbol(&slotRawObject(&g->block->selectors)->slots[op3]);

            slot = g->sp - numArgsPushed + 1;
            classobj = slotRawSymbol(&slotRawClass(&g->method->ownerclass)->superclass)->u.classobj;

            if (numKeyArgsPushed)
                goto key_msg_lookup;
            else
                goto msg_lookup;
        }

        case 12: // opExtended, opSendSpecialMsg
        handle_op_12 : {
            numArgsPushed = ip[1]; // get num args
            numKeyArgsPushed = ip[2]; // get num keyword args
            int op3 = ip[3]; // get selector index
            ip += 3;

            selector = gSpecialSelectors[op3];
            slot = sp - numArgsPushed + 1;

            if (numKeyArgsPushed)
                goto key_class_lookup;
            else
                goto class_lookup;
        }

        case 13: // opExtended, opSendSpecialUnaryArithMsg
        handle_op_13 : {
            int op2 = ip[1];
            ++ip; // get selector index
            g->sp = sp;
            g->ip = ip;
            g->primitiveIndex = op2;
            doSpecialUnaryArithMsg(g, -1);
#if TAILCALLOPTIMIZE
            g->tailCall = 0;
#endif
            sp = g->sp;
            ip = g->ip;
            dispatch_opcode;
        }
        case 14: // opExtended, opSendSpecialBinaryArithMsg
        handle_op_14 : {
            int op2 = ip[1];
            ++ip; // get selector index
            g->sp = sp;
            g->ip = ip;
            g->primitiveIndex = op2;
            doSpecialBinaryArithMsg(g, 2, false);
            sp = g->sp;
            ip = g->ip;
            dispatch_opcode;
        }
        case 15: // opExtended, opSpecialOpcode (none yet)
        handle_op_15 : {
            int op2 = ip[1];
            ++ip; // get extended special opcode
            switch (op2) {
            case opgProcess: // push thisProcess
                ++sp;
                SetObject(sp, g->process);
                break;
            case opgThread: // push thisProcess
                ++sp;
                SetObject(sp, g->thread);
                break;
            case opgMethod: // push thisMethod
                ++sp;
                SetObject(sp, g->method);
                break;
            case opgFunctionDef: // push thisFunctionDef
                ++sp;
                SetObject(sp, g->block);
                break;
            case opgFunction: { // push thisFunc
                // push a closure
                g->sp = sp; // gc may push the stack
                PyrClosure* closure = (PyrClosure*)g->gc->New(2 * sizeof(PyrSlot), 0, obj_notindexed, true);
                sp = g->sp;
                closure->classptr = gSpecialClasses[op_class_func]->u.classobj;
                closure->size = 2;
                SetObject(&closure->block, g->block);
                SetObject(&closure->context, slotRawFrame(&g->frame->context));
                ++sp;
                SetObject(sp, closure);
                break;
            }
            default:
                slotCopy(++sp, &gSpecialValues[svNil]);
                break;
            }
            dispatch_opcode;
        }
        // opPushInstVar, 0..15
        case 16:
        handle_op_16:
            slotCopy(++sp, &slotRawObject(&g->receiver)->slots[0]);
            dispatch_opcode;
        case 17:
        handle_op_17:
            slotCopy(++sp, &slotRawObject(&g->receiver)->slots[1]);
            dispatch_opcode;
        case 18:
        handle_op_18:
            slotCopy(++sp, &slotRawObject(&g->receiver)->slots[2]);
            dispatch_opcode;
        case 19:
        handle_op_19:
            slotCopy(++sp, &slotRawObject(&g->receiver)->slots[3]);
            dispatch_opcode;
        case 20:
        handle_op_20:
            slotCopy(++sp, &slotRawObject(&g->receiver)->slots[4]);
            dispatch_opcode;
        case 21:
        handle_op_21:
            slotCopy(++sp, &slotRawObject(&g->receiver)->slots[5]);
            dispatch_opcode;
        case 22:
        handle_op_22:
            slotCopy(++sp, &slotRawObject(&g->receiver)->slots[6]);
            dispatch_opcode;
        case 23:
        handle_op_23:
            slotCopy(++sp, &slotRawObject(&g->receiver)->slots[7]);
            dispatch_opcode;
        case 24:
        handle_op_24:
            slotCopy(++sp, &slotRawObject(&g->receiver)->slots[8]);
            dispatch_opcode;
        case 25:
        handle_op_25:
            slotCopy(++sp, &slotRawObject(&g->receiver)->slots[9]);
            dispatch_opcode;
        case 26:
        handle_op_26:
            slotCopy(++sp, &slotRawObject(&g->receiver)->slots[10]);
            dispatch_opcode;
        case 27:
        handle_op_27:
            slotCopy(++sp, &slotRawObject(&g->receiver)->slots[11]);
            dispatch_opcode;
        case 28:
        handle_op_28:
            slotCopy(++sp, &slotRawObject(&g->receiver)->slots[12]);
            dispatch_opcode;
        case 29:
        handle_op_29:
            slotCopy(++sp, &slotRawObject(&g->receiver)->slots[13]);
            dispatch_opcode;
        case 30:
        handle_op_30:
            slotCopy(++sp, &slotRawObject(&g->receiver)->slots[14]);
            dispatch_opcode;
        case 31:
        handle_op_31:
            slotCopy(++sp, &slotRawObject(&g->receiver)->slots[15]);
            dispatch_opcode;

        case 32: // JumpIfTrue
        handle_op_32:
            // cannot compare with o_false because it is NaN
            if (IsTrue(sp)) {
                int jmplen = (ip[1] << 8) | ip[2];
                ip += jmplen + 2;
            } else if (IsFalse(sp)) {
                ip += 2;
            } else {
                numArgsPushed = 1;
                selector = gSpecialSelectors[opmNonBooleanError];
                slot = sp;

                goto class_lookup;
            }
            --sp;
            dispatch_opcode;

        // opPushTempVar, levels 1..7
        case 33:
        handle_op_33:
            slotCopy(++sp, &slotRawFrame(&g->frame->context)->vars[ip[1]]);
            ++ip;
            dispatch_opcode;
        case 34:
        handle_op_34:
            slotCopy(++sp, &slotRawFrame(&slotRawFrame(&g->frame->context)->context)->vars[ip[1]]);
            ++ip;
            dispatch_opcode;
        case 35:
        handle_op_35:
            slotCopy(++sp,
                     &slotRawFrame(&slotRawFrame(&slotRawFrame(&g->frame->context)->context)->context)->vars[ip[1]]);
            ++ip;
            dispatch_opcode;
        case 36:
        handle_op_36:
            slotCopy(++sp,
                     &slotRawFrame(
                          &slotRawFrame(&slotRawFrame(&slotRawFrame(&g->frame->context)->context)->context)->context)
                          ->vars[ip[1]]);
            ++ip;
            dispatch_opcode;
        case 37:
        handle_op_37:
            slotCopy(
                ++sp,
                &slotRawFrame(
                     &slotRawFrame(
                          &slotRawFrame(&slotRawFrame(&slotRawFrame(&g->frame->context)->context)->context)->context)
                          ->context)
                     ->vars[ip[1]]);
            ++ip;
            dispatch_opcode;
        case 38:
        handle_op_38:
            slotCopy(++sp,
                     &slotRawFrame(
                          &slotRawFrame(
                               &slotRawFrame(
                                    &slotRawFrame(&slotRawFrame(&slotRawFrame(&g->frame->context)->context)->context)
                                         ->context)
                                    ->context)
                               ->context)
                          ->vars[ip[1]]);
            ++ip;
            dispatch_opcode;
        case 39:
        handle_op_39:
            slotCopy(
                ++sp,
                &slotRawFrame(
                     &slotRawFrame(
                          &slotRawFrame(
                               &slotRawFrame(
                                    &slotRawFrame(&slotRawFrame(&slotRawFrame(&g->frame->context)->context)->context)
                                         ->context)
                                    ->context)
                               ->context)
                          ->context)
                     ->vars[ip[1]]);
            ++ip;
            dispatch_opcode;

        // push literal constants.
        case 40:
        handle_op_40 : {
            int ival = ip[1];
            ip += 1;
            slotCopy(++sp, &slotRawObject(&g->block->constants)->slots[ival]);
            dispatch_opcode;
        }
        case 41:
        handle_op_41 : {
            int ival = (ip[1] << 8) | ip[2];
            ip += 2;
            slotCopy(++sp, &slotRawObject(&g->block->constants)->slots[ival]);
            dispatch_opcode;
        }
        case 42:
        handle_op_42 : {
            int ival = (ip[1] << 16) | (ip[2] << 8) | ip[3];
            ip += 3;
            slotCopy(++sp, &slotRawObject(&g->block->constants)->slots[ival]);
            dispatch_opcode;
        }
        case 43:
        handle_op_43 : {
            int ival = (ip[1] << 24) | (ip[2] << 16) | (ip[3] << 8) | ip[4];
            ip += 4;
            slotCopy(++sp, &slotRawObject(&g->block->constants)->slots[ival]);
            dispatch_opcode;
        }
        // push integers.
        case 44:
        handle_op_44 : {
            int ival = (int32)(ip[1] << 24) >> 24;
            ip += 1;
            ++sp;
            SetInt(sp, ival);
            dispatch_opcode;
        }
        case 45:
        handle_op_45 : {
            int ival = (int32)((ip[1] << 24) | (ip[2] << 16)) >> 16;
            ip += 2;
            ++sp;
            SetInt(sp, ival);
            dispatch_opcode;
        }
        case 46:
        handle_op_46 : {
            int ival = (int32)((ip[1] << 24) | (ip[2] << 16) | (ip[3] << 8)) >> 8;
            ip += 3;
            ++sp;
            SetInt(sp, ival);
            dispatch_opcode;
        }
        case 47:
        handle_op_47 : {
            int ival = (int32)((ip[1] << 24) | (ip[2] << 16) | (ip[3] << 8) | ip[4]);
            ip += 4;
            ++sp;
            SetInt(sp, ival);
            dispatch_opcode;
        }

        // opPushTempZeroVar
        case 48:
        handle_op_48:
            slotCopy(++sp, &g->frame->vars[0]);
            dispatch_opcode;
        case 49:
        handle_op_49:
            slotCopy(++sp, &g->frame->vars[1]);
            dispatch_opcode;
        case 50:
        handle_op_50:
            slotCopy(++sp, &g->frame->vars[2]);
            dispatch_opcode;
        case 51:
        handle_op_51:
            slotCopy(++sp, &g->frame->vars[3]);
            dispatch_opcode;
        case 52:
        handle_op_52:
            slotCopy(++sp, &g->frame->vars[4]);
            dispatch_opcode;
        case 53:
        handle_op_53:
            slotCopy(++sp, &g->frame->vars[5]);
            dispatch_opcode;
        case 54:
        handle_op_54:
            slotCopy(++sp, &g->frame->vars[6]);
            dispatch_opcode;
        case 55:
        handle_op_55:
            slotCopy(++sp, &g->frame->vars[7]);
            dispatch_opcode;
        case 56:
        handle_op_56:
            slotCopy(++sp, &g->frame->vars[8]);
            dispatch_opcode;
        case 57:
        handle_op_57:
            slotCopy(++sp, &g->frame->vars[9]);
            dispatch_opcode;
        case 58:
        handle_op_58:
            slotCopy(++sp, &g->frame->vars[10]);
            dispatch_opcode;
        case 59:
        handle_op_59:
            slotCopy(++sp, &g->frame->vars[11]);
            dispatch_opcode;
        case 60:
        handle_op_60:
            slotCopy(++sp, &g->frame->vars[12]);
            dispatch_opcode;
        case 61:
        handle_op_61:
            slotCopy(++sp, &g->frame->vars[13]);
            dispatch_opcode;
        case 62:
        handle_op_62:
            slotCopy(++sp, &g->frame->vars[14]);
            dispatch_opcode;
        case 63:
        handle_op_63:
            slotCopy(++sp, &g->frame->vars[15]);
            dispatch_opcode;

        // case opPushLiteral
        case 64:
        handle_op_64:
            slotCopy(++sp, &slotRawObject(&g->block->constants)->slots[0]);
            dispatch_opcode;
        case 65:
        handle_op_65:
            slotCopy(++sp, &slotRawObject(&g->block->constants)->slots[1]);
            dispatch_opcode;
        case 66:
        handle_op_66:
            slotCopy(++sp, &slotRawObject(&g->block->constants)->slots[2]);
            dispatch_opcode;
        case 67:
        handle_op_67:
            slotCopy(++sp, &slotRawObject(&g->block->constants)->slots[3]);
            dispatch_opcode;
        case 68:
        handle_op_68:
            slotCopy(++sp, &slotRawObject(&g->block->constants)->slots[4]);
            dispatch_opcode;
        case 69:
        handle_op_69:
            slotCopy(++sp, &slotRawObject(&g->block->constants)->slots[5]);
            dispatch_opcode;
        case 70:
        handle_op_70:
            slotCopy(++sp, &slotRawObject(&g->block->constants)->slots[6]);
            dispatch_opcode;
        case 71:
        handle_op_71:
            slotCopy(++sp, &slotRawObject(&g->block->constants)->slots[7]);
            dispatch_opcode;
        case 72:
        handle_op_72:
            slotCopy(++sp, &slotRawObject(&g->block->constants)->slots[8]);
            dispatch_opcode;
        case 73:
        handle_op_73:
            slotCopy(++sp, &slotRawObject(&g->block->constants)->slots[9]);
            dispatch_opcode;
        case 74:
        handle_op_74:
            slotCopy(++sp, &slotRawObject(&g->block->constants)->slots[10]);
            dispatch_opcode;
        case 75:
        handle_op_75:
            slotCopy(++sp, &slotRawObject(&g->block->constants)->slots[11]);
            dispatch_opcode;
        case 76:
        handle_op_76:
            slotCopy(++sp, &slotRawObject(&g->block->constants)->slots[12]);
            dispatch_opcode;
        case 77:
        handle_op_77:
            slotCopy(++sp, &slotRawObject(&g->block->constants)->slots[13]);
            dispatch_opcode;
        case 78:
        handle_op_78:
            slotCopy(++sp, &slotRawObject(&g->block->constants)->slots[14]);
            dispatch_opcode;
        case 79:
        handle_op_79:
            slotCopy(++sp, &slotRawObject(&g->block->constants)->slots[15]);
            dispatch_opcode;

        // opPushClassVar
        case 80:
        handle_op_80:
            handlePushClassVar(g, sp, ip, 0);
            dispatch_opcode;
        case 81:
        handle_op_81:
            handlePushClassVar(g, sp, ip, 1);
            dispatch_opcode;
        case 82:
        handle_op_82:
            handlePushClassVar(g, sp, ip, 2);
            dispatch_opcode;
        case 83:
        handle_op_83:
            handlePushClassVar(g, sp, ip, 3);
            dispatch_opcode;
        case 84:
        handle_op_84:
            handlePushClassVar(g, sp, ip, 4);
            dispatch_opcode;
        case 85:
        handle_op_85:
            handlePushClassVar(g, sp, ip, 5);
            dispatch_opcode;
        case 86:
        handle_op_86:
            handlePushClassVar(g, sp, ip, 6);
            dispatch_opcode;
        case 87:
        handle_op_87:
            handlePushClassVar(g, sp, ip, 7);
            dispatch_opcode;
        case 88:
        handle_op_88:
            handlePushClassVar(g, sp, ip, 8);
            dispatch_opcode;
        case 89:
        handle_op_89:
            handlePushClassVar(g, sp, ip, 9);
            dispatch_opcode;
        case 90:
        handle_op_90:
            handlePushClassVar(g, sp, ip, 10);
            dispatch_opcode;
        case 91:
        handle_op_91:
            handlePushClassVar(g, sp, ip, 11);
            dispatch_opcode;
        case 92:
        handle_op_92:
            handlePushClassVar(g, sp, ip, 12);
            dispatch_opcode;
        case 93:
        handle_op_93:
            handlePushClassVar(g, sp, ip, 13);
            dispatch_opcode;
        case 94:
        handle_op_94:
            handlePushClassVar(g, sp, ip, 14);
            dispatch_opcode;
        case 95:
        handle_op_95:
            handlePushClassVar(g, sp, ip, 15);
            dispatch_opcode;

        // opPushSpecialValue
        case 96:
        handle_op_96:
            slotCopy(++sp, &g->receiver);
            dispatch_opcode;
        case 97: // push one and subtract
        handle_op_97:
            if (IsInt(sp)) {
                SetRaw(sp, slotRawInt(sp) - 1);
#if TAILCALLOPTIMIZE
                g->tailCall = 0;
#endif
            } else {
                slotCopy(++sp, &gSpecialValues[svOne]);
                g->sp = sp;
                g->ip = ip;
                g->primitiveIndex = opSub;
                prSubNum(g, -1);
                sp = g->sp;
                ip = g->ip;
            }
            dispatch_opcode;
        case 98:
        handle_op_98:
            slotCopy(++sp, &gSpecialValues[svNegOne]);
            dispatch_opcode;
        case 99:
        handle_op_99:
            slotCopy(++sp, &gSpecialValues[svZero]);
            dispatch_opcode;
        case 100:
        handle_op_100:
            slotCopy(++sp, &gSpecialValues[svOne]);
            dispatch_opcode;
        case 101:
        handle_op_101:
            slotCopy(++sp, &gSpecialValues[svTwo]);
            dispatch_opcode;
        case 102:
        handle_op_102:
            slotCopy(++sp, &gSpecialValues[svFHalf]);
            dispatch_opcode;
        case 103:
        handle_op_103:
            slotCopy(++sp, &gSpecialValues[svFNegOne]);
            dispatch_opcode;
        case 104:
        handle_op_104:
            slotCopy(++sp, &gSpecialValues[svFZero]);
            dispatch_opcode;
        case 105:
        handle_op_105:
            slotCopy(++sp, &gSpecialValues[svFOne]);
            dispatch_opcode;
        case 106:
        handle_op_106:
            slotCopy(++sp, &gSpecialValues[svFTwo]);
            dispatch_opcode;
        case 107: // push one and add
        handle_op_107:
            if (IsInt(sp)) {
                SetRaw(sp, slotRawInt(sp) + 1);
#if TAILCALLOPTIMIZE
                g->tailCall = 0;
#endif
            } else {
                slotCopy(++sp, &gSpecialValues[svOne]);
                g->sp = sp;
                g->ip = ip;
                g->primitiveIndex = opAdd;
                prAddNum(g, -1);
                sp = g->sp;
                ip = g->ip;
            }
            dispatch_opcode;
        case 108:
        handle_op_108:
            slotCopy(++sp, &gSpecialValues[svTrue]);
            dispatch_opcode;
        case 109:
        handle_op_109:
            slotCopy(++sp, &gSpecialValues[svFalse]);
            dispatch_opcode;
        case 110:
        handle_op_110:
            slotCopy(++sp, &gSpecialValues[svNil]);
            dispatch_opcode;
        case 111:
        handle_op_111:
            slotCopy(++sp, &gSpecialValues[svInf]);
            dispatch_opcode;

        // opStoreInstVar, 0..15
        case 112:
        handle_op_112:
            handleStoreInstVar(g, sp, ip, 0);
            dispatch_opcode;
        case 113:
        handle_op_113:
            handleStoreInstVar(g, sp, ip, 1);
            dispatch_opcode;
        case 114:
        handle_op_114:
            handleStoreInstVar(g, sp, ip, 2);
            dispatch_opcode;
        case 115:
        handle_op_115:
            handleStoreInstVar(g, sp, ip, 3);
            dispatch_opcode;
        case 116:
        handle_op_116:
            handleStoreInstVar(g, sp, ip, 4);
            dispatch_opcode;
        case 117:
        handle_op_117:
            handleStoreInstVar(g, sp, ip, 5);
            dispatch_opcode;
        case 118:
        handle_op_118:
            handleStoreInstVar(g, sp, ip, 6);
            dispatch_opcode;
        case 119:
        handle_op_119:
            handleStoreInstVar(g, sp, ip, 7);
            dispatch_opcode;
        case 120:
        handle_op_120:
            handleStoreInstVar(g, sp, ip, 8);
            dispatch_opcode;
        case 121:
        handle_op_121:
            handleStoreInstVar(g, sp, ip, 9);
            dispatch_opcode;
        case 122:
        handle_op_122:
            handleStoreInstVar(g, sp, ip, 10);
            dispatch_opcode;
        case 123:
        handle_op_123:
            handleStoreInstVar(g, sp, ip, 11);
            dispatch_opcode;
        case 124:
        handle_op_124:
            handleStoreInstVar(g, sp, ip, 12);
            dispatch_opcode;
        case 125:
        handle_op_125:
            handleStoreInstVar(g, sp, ip, 13);
            dispatch_opcode;
        case 126:
        handle_op_126:
            handleStoreInstVar(g, sp, ip, 14);
            dispatch_opcode;
        case 127:
        handle_op_127:
            handleStoreInstVar(g, sp, ip, 15);
            dispatch_opcode;

        // opStoreTempVar
        case 128:
        handle_op_128 : {
            int op3 = ip[1];
            ++ip; // get temp var index
            PyrFrame* tframe = g->frame; // zero level
            slot = tframe->vars + op3;
            slotCopy(slot, sp--);
            g->gc->GCWrite(tframe, slot);
            dispatch_opcode;
        }

        case 129:
        handle_op_129 : {
            int op3 = ip[1];
            ++ip; // get temp var index
            PyrFrame* tframe = slotRawFrame(&g->frame->context); // one level
            slot = tframe->vars + op3;
            slotCopy(slot, sp--);
            g->gc->GCWrite(tframe, slot);
            dispatch_opcode;
        }

        case 130:
        handle_op_130 : {
            int op3 = ip[1];
            ++ip; // get temp var index
            PyrFrame* tframe = slotRawFrame(&slotRawFrame(&g->frame->context)->context); // two levels
            slot = tframe->vars + op3;
            slotCopy(slot, sp--);
            g->gc->GCWrite(tframe, slot);
            dispatch_opcode;
        }

        case 131:
        handle_op_131 : {
            int op3 = ip[1];
            ++ip; // get temp var index
            PyrFrame* tframe =
                slotRawFrame(&slotRawFrame(&slotRawFrame(&g->frame->context)->context)->context); // three levels
            slot = tframe->vars + op3;
            slotCopy(slot, sp--);
            g->gc->GCWrite(tframe, slot);
            dispatch_opcode;
        }

        case 132:
        handle_op_132 : {
            int op3 = ip[1];
            ++ip; // get temp var index
            PyrFrame* tframe =
                slotRawFrame(&slotRawFrame(&slotRawFrame(&slotRawFrame(&g->frame->context)->context)->context)
                                  ->context); // four levels
            slot = tframe->vars + op3;
            slotCopy(slot, sp--);
            g->gc->GCWrite(tframe, slot);
            dispatch_opcode;
        }

        case 133:
        case 134:
        case 135:
        handle_op_133:
        handle_op_134:
        handle_op_135 : {
            int op2 = op1 & 15;
            int op3 = ip[1];
            ++ip; // get temp var index
            PyrFrame* tframe = g->frame;
            for (; op2--; tframe = slotRawFrame(&tframe->context)) { /* noop */
            }
            slot = tframe->vars + op3;
            slotCopy(slot, sp);
            g->gc->GCWrite(tframe, slot);
            dispatch_opcode;
        }

        case 136: // push inst var, send special selector
        handle_op_136 : {
            int op2 = ip[1]; // get inst var index
            int op3 = ip[2]; // get selector
            ip += 2;

            slotCopy(++sp, &slotRawObject(&g->receiver)->slots[op2]);

            numArgsPushed = 1;
            selector = gSpecialSelectors[op3];
            slot = sp;

            goto class_lookup;
        }

        case 137: // push all args, send msg
        handle_op_137 : {
            numArgsPushed = METHRAW(g->block)->numargs;
            PyrSlot* pslot = g->frame->vars - 1;
            for (int m = 0; m < numArgsPushed; ++m)
                *++sp = *++pslot;

            int op2 = ip[1];
            ++ip; // get selector index
            selector = slotRawSymbol(&slotRawObject(&g->block->selectors)->slots[op2]);
            slot = sp - numArgsPushed + 1;

            goto class_lookup;
        }
        case 138: // push all but first arg, send msg
        handle_op_138 : {
            numArgsPushed = METHRAW(g->block)->numargs;
            PyrSlot* pslot = g->frame->vars;
            for (int m = 0; m < numArgsPushed - 1; ++m)
                *++sp = *++pslot;

            int op2 = ip[1];
            ++ip; // get selector index
            selector = slotRawSymbol(&slotRawObject(&g->block->selectors)->slots[op2]);
            slot = sp - numArgsPushed + 1;

            goto class_lookup;
        }

        case 139: // push all args, send special
        handle_op_139 : {
            numArgsPushed = METHRAW(g->block)->numargs;
            PyrSlot* pslot = g->frame->vars - 1;
            for (int m = 0; m < numArgsPushed; ++m)
                *++sp = *++pslot;

            int op2 = ip[1];
            ++ip; // get selector
            selector = gSpecialSelectors[op2];
            slot = sp - numArgsPushed + 1;

            goto class_lookup;
        }

        case 140: // push all but first arg, send special
        handle_op_140 : {
            numArgsPushed = METHRAW(g->block)->numargs;
            PyrSlot* pslot = g->frame->vars;
            for (int m = 0; m < numArgsPushed - 1; ++m)
                *++sp = *++pslot;

            int op2 = ip[1];
            ++ip; // get selector
            selector = gSpecialSelectors[op2];
            slot = sp - numArgsPushed + 1;

            goto class_lookup;
        }

        case 141: // one arg pushed, push all but first arg, send msg
        handle_op_141 : {
            numArgsPushed = METHRAW(g->block)->numargs + 1;
            PyrSlot* pslot = g->frame->vars;
            for (int m = 0; m < numArgsPushed - 2; ++m)
                *++sp = *++pslot;

            int op2 = ip[1];
            ++ip; // get selector index
            selector = slotRawSymbol(&slotRawObject(&g->block->selectors)->slots[op2]);
            slot = sp - numArgsPushed + 1;

            goto class_lookup;
        }

        case 142: // one arg pushed, push all but first arg, send special
        handle_op_142 : {
            numArgsPushed = METHRAW(g->block)->numargs + 1;
            PyrSlot* pslot = g->frame->vars;
            for (int m = 0; m < numArgsPushed - 2; ++m)
                *++sp = *++pslot;

            int op2 = ip[1];
            ++ip; // get selector
            selector = gSpecialSelectors[op2];
            slot = sp - numArgsPushed + 1;

            goto class_lookup;
        }

        case 143: // loop byte codes
        handle_op_143 : {
            // this is major cheating to speed up often used looping methods
            // these byte codes are specific to their method and should only be used there.
            int op2 = ip[1];
            ++ip; // get which one
            switch (op2) {
            // Integer-do : 143 0, 143 1
            case 0: {
                PyrSlot* vars = g->frame->vars;
                if (slotRawInt(&vars[2]) < slotRawInt(&g->receiver)) {
                    slotCopy(++sp, &vars[1]); // push function
                    slotCopy(++sp, &vars[2]); // push i
                    slotCopy(++sp, &vars[2]); // push i
                    // SendSpecialMsg value
                    numArgsPushed = 3;
                    selector = gSpecialSelectors[opmValue];
                    slot = sp - 2;

                    goto class_lookup;
                } else {
                    slotCopy(++sp, &g->receiver);
                    g->sp = sp;
                    g->ip = ip;
                    returnFromMethod(g);
                    sp = g->sp;
                    ip = g->ip;
                }
                dispatch_opcode;
            }
            case 1:
                --sp; // Drop
                SetRaw(&g->frame->vars[2], slotRawInt(&g->frame->vars[2]) + 1); // inc i
                ip -= 4;
                dispatch_opcode;

            // Integer-reverseDo : 143 2, 143 3, 143 4
            case 2:
                SetRaw(&g->frame->vars[2], slotRawInt(&g->receiver) - 1);
                dispatch_opcode;
            case 3: {
                PyrSlot* vars = g->frame->vars;
                if (slotRawInt(&vars[2]) >= 0) {
                    slotCopy(++sp, &vars[1]); // push function
                    slotCopy(++sp, &vars[2]); // push i
                    slotCopy(++sp, &vars[3]); // push j
                    // SendSpecialMsg value
                    numArgsPushed = 3;
                    selector = gSpecialSelectors[opmValue];
                    slot = sp - 2;

                    goto class_lookup;
                } else {
                    slotCopy(++sp, &g->receiver);
                    g->sp = sp;
                    g->ip = ip;
                    returnFromMethod(g);
                    sp = g->sp;
                    ip = g->ip;
                }
                dispatch_opcode;
            }
            case 4: {
                --sp; // Drop
                PyrSlot* vars = g->frame->vars;
                SetRaw(&vars[2], slotRawInt(&vars[2]) - 1); // dec i
                SetRaw(&vars[3], slotRawInt(&vars[3]) + 1); // inc j
                ip -= 4;
                dispatch_opcode;
            }
            // Integer-for : 143 5, 143 6, 143 16
            case 5: {
                PyrSlot* vars = g->frame->vars;
                int tag = GetTag(&vars[1]);

                if (tag != tagInt) {
                    if (IsFloat(&vars[1])) {
                        // coerce to int
                        SetInt(&vars[1], (int32)(slotRawFloat(&vars[1])));
                    } else {
                        error("Integer-for : endval not a SimpleNumber.\n");

                        slotCopy(++sp, &g->receiver);
                        numArgsPushed = 1;
                        selector = gSpecialSelectors[opmPrimitiveFailed];
                        slot = sp;

                        goto class_lookup;
                    }
                }

                if (slotRawInt(&g->receiver) <= slotRawInt(&vars[1])) {
                    SetRaw(&vars[5], 1);
                } else {
                    SetRaw(&vars[5], -1);
                }
                slotCopy(&vars[3], &g->receiver);

                dispatch_opcode;
            }
            case 6: {
                PyrSlot* vars = g->frame->vars;
                if ((slotRawInt(&vars[5]) > 0 && slotRawInt(&vars[3]) <= slotRawInt(&vars[1]))
                    || (slotRawInt(&vars[5]) < 0 && slotRawInt(&vars[3]) >= slotRawInt(&vars[1]))) {
                    slotCopy(++sp, &vars[2]); // push function
                    slotCopy(++sp, &vars[3]); // push i
                    slotCopy(++sp, &vars[4]); // push j
                    // SendSpecialMsg value
                    numArgsPushed = 3;
                    selector = gSpecialSelectors[opmValue];
                    slot = sp - 2;

                    goto class_lookup;
                } else {
                    slotCopy(++sp, &g->receiver);
                    g->sp = sp;
                    g->ip = ip;
                    returnFromMethod(g);
                    sp = g->sp;
                    ip = g->ip;
                }
                dispatch_opcode;
            }

            // Integer-forBy : 143 7, 143 8, 143 9
            case 7: {
                PyrSlot* vars = g->frame->vars;
                // Detect case of a zero-valued stepval argument, which
                // would result in an infinite loop.
                if ((IsFloat(vars + 2) && slotRawFloat(&vars[2]) == 0.0)
                    || (IsInt(vars + 2) && slotRawInt(&vars[2]) == 0)) {
                    error("Integer-forBy: zero-valued stepval argument.\n");
                    slotCopy(++sp, &g->receiver);
                    numArgsPushed = 1;
                    selector = gSpecialSelectors[opmPrimitiveFailed];
                    slot = sp;

                    goto class_lookup;
                }
                // If any argument is floating point we cast all arguments
                // to floating point, including the accumulator. This avoids
                // potential infinite loops due to integer truncation.
                if (IsFloat(vars + 1) || IsFloat(vars + 2)) {
                    if (IsInt(vars + 1)) {
                        SetFloat(&vars[1], (double)(slotRawInt(&vars[1])));
                    }
                    if (IsInt(vars + 2)) {
                        SetFloat(&vars[2], (double)(slotRawInt(&vars[2])));
                    }
                    SetFloat(&vars[4], (double)(slotRawInt(&g->receiver)));
                } else {
                    int tag = GetTag(&vars[1]);
                    if ((tag != tagInt) || NotInt(&vars[2])) {
                        error("Integer-forBy : endval or stepval not an Integer or Float.\n");

                        slotCopy(++sp, &g->receiver);
                        numArgsPushed = 1;
                        selector = gSpecialSelectors[opmPrimitiveFailed];
                        slot = sp;

                        goto class_lookup;
                    }
                    slotCopy(&vars[4], &g->receiver);
                }
                dispatch_opcode;
            }
            case 8: {
                PyrSlot* vars = g->frame->vars;
                bool continueForBy = false;
                if (IsFloat(vars + 4)) {
                    continueForBy = (slotRawFloat(&vars[2]) >= 0.0 && slotRawFloat(&vars[4]) <= slotRawFloat(&vars[1]))
                        || (slotRawFloat(&vars[2]) < 0.0 && slotRawFloat(&vars[4]) >= slotRawFloat(&vars[1]));
                } else {
                    continueForBy = (slotRawInt(&vars[2]) >= 0 && slotRawInt(&vars[4]) <= slotRawInt(&vars[1]))
                        || (slotRawInt(&vars[2]) < 0 && slotRawInt(&vars[4]) >= slotRawInt(&vars[1]));
                }
                if (continueForBy) {
                    slotCopy(++sp, &vars[3]); // push function
                    slotCopy(++sp, &vars[4]); // push i
                    slotCopy(++sp, &vars[5]); // push j
                    // SendSpecialMsg value
                    numArgsPushed = 3;
                    selector = gSpecialSelectors[opmValue];
                    slot = sp - 2;

                    goto class_lookup;
                } else {
                    slotCopy(++sp, &g->receiver);
                    g->sp = sp;
                    g->ip = ip;
                    returnFromMethod(g);
                    sp = g->sp;
                    ip = g->ip;
                }
                dispatch_opcode;
            }
            case 9: {
                --sp; // Drop
                PyrSlot* vars = g->frame->vars;
                if (IsFloat(vars + 4)) {
                    SetRaw(&vars[4], slotRawFloat(&vars[4]) + slotRawFloat(&vars[2]));
                } else {
                    SetRaw(&vars[4], slotRawInt(&vars[4]) + slotRawInt(&vars[2])); // inc i
                }
                SetRaw(&vars[5], slotRawInt(&vars[5]) + 1); // inc j
                ip -= 4;
                dispatch_opcode;
            }
            // ArrayedCollection-do : 143 10, 143 1
            case 10: {
                // 0 this, 1 func, 2 i
                PyrSlot* vars = g->frame->vars;

                if (slotRawInt(&vars[2]) < slotRawObject(&g->receiver)->size) {
                    slotCopy(++sp, &vars[1]); // push function
                    getIndexedSlot(slotRawObject(&g->receiver), ++sp, slotRawInt(&vars[2])); // push this.at(i)
                    slotCopy(++sp, &vars[2]); // push i
                    // SendSpecialMsg value
                    numArgsPushed = 3;
                    selector = gSpecialSelectors[opmValue];
                    slot = sp - 2;

                    goto class_lookup;
                } else {
                    slotCopy(++sp, &g->receiver);
                    g->sp = sp;
                    g->ip = ip;
                    returnFromMethod(g);
                    sp = g->sp;
                    ip = g->ip;
                }
                dispatch_opcode;
            }

            // ArrayedCollection-reverseDo : 143 11, 143 12, 143 4
            case 11:
                SetRaw(&g->frame->vars[2], slotRawObject(&g->receiver)->size - 1);
                dispatch_opcode;
            case 12: {
                PyrSlot* vars = g->frame->vars;
                if (slotRawInt(&vars[2]) >= 0) {
                    slotCopy(++sp, &vars[1]); // push function
                    getIndexedSlot(slotRawObject(&g->receiver), ++sp, slotRawInt(&vars[2])); // push this.at(i)
                    slotCopy(++sp, &vars[3]); // push j
                    // SendSpecialMsg value
                    numArgsPushed = 3;
                    selector = gSpecialSelectors[opmValue];
                    slot = sp - 2;

                    goto class_lookup; // class_lookup:
                } else {
                    slotCopy(++sp, &g->receiver);
                    g->sp = sp;
                    g->ip = ip;
                    returnFromMethod(g);
                    sp = g->sp;
                    ip = g->ip;
                }
                dispatch_opcode;
            }

            // Dictionary-keysValuesArrayDo
            case 13: {
                PyrSlot* vars = g->frame->vars;

                if (IsNil(&vars[1])) {
                    error("Dictionary-keysValuesArrayDo: first argument should not be nil.\n");

                    slotCopy(++sp, &g->receiver);
                    numArgsPushed = 0;
                    selector = gSpecialSelectors[opmPrimitiveFailed];
                    slot = sp;

                    goto class_lookup;
                }

                int m = slotRawInt(&vars[3]);
                PyrObject* obj = slotRawObject(&vars[1]);


                if (m < obj->size) {
                    slot = obj->slots + m; // key
                    while (IsNil(slot)) {
                        m += 2;
                        if (m >= obj->size) {
                            SetRaw(&vars[3], m);
                            goto keysValuesArrayDo_return;
                        }
                        slot = obj->slots + m; // key
                    }
                    SetRaw(&vars[3], m);
                    slotCopy(++sp, &vars[2]); // function
                    slotCopy(++sp, &slot[0]); // key
                    slotCopy(++sp, &slot[1]); // val
                    slotCopy(++sp, &vars[4]); // j
                    SetRaw(&vars[4], slotRawInt(&vars[4]) + 1);

                    // SendSpecialMsg value
                    numArgsPushed = 4;
                    selector = gSpecialSelectors[opmValue];
                    slot = sp - 3;

                    goto class_lookup; // class_lookup:
                } else {
                keysValuesArrayDo_return:
                    slotCopy(++sp, &g->receiver);
                    g->sp = sp;
                    g->ip = ip;
                    returnFromMethod(g);
                    sp = g->sp;
                    ip = g->ip;
                }
                dispatch_opcode;
            }
            case 14:
                --sp; // Drop
                SetRaw(&g->frame->vars[3], slotRawInt(&g->frame->vars[3]) + 2); // inc i
                ip -= 4;
                dispatch_opcode;
            case 15:
                // unused opcode.
                break;

            case 16: {
                --sp; // Drop
                PyrSlot* vars = g->frame->vars;
                SetRaw(&vars[3], slotRawInt(&vars[3]) + slotRawInt(&vars[5])); // inc i by stepval
                SetRaw(&vars[4], slotRawInt(&vars[4]) + 1); // inc j
                ip -= 4;
                dispatch_opcode;
            }

            // Float-do : 143 17, 143 18
            case 17: {
                PyrSlot* vars = g->frame->vars;
                if (slotRawFloat(&vars[2]) + 0.5 < slotRawFloat(&g->receiver)) {
                    slotCopy(++sp, &vars[1]); // push function
                    slotCopy(++sp, &vars[2]); // push i
                    slotCopy(++sp, &vars[2]); // push i
                    // SendSpecialMsg value
                    numArgsPushed = 3;
                    selector = gSpecialSelectors[opmValue];
                    slot = sp - 2;

                    goto class_lookup;
                } else {
                    slotCopy(++sp, &g->receiver);
                    g->sp = sp;
                    g->ip = ip;
                    returnFromMethod(g);
                    sp = g->sp;
                    ip = g->ip;
                }
                dispatch_opcode;
            }
            case 18: {
                --sp; // Drop
                SetRaw(&g->frame->vars[2], slotRawFloat(&g->frame->vars[2]) + 1.0); // inc i
                ip -= 4;
                dispatch_opcode;
            }

            // Float-reverseDo : 143 19, 143 20, 143 21
            case 19: {
                SetFloat(&g->frame->vars[2], slotRawFloat(&g->receiver) - 1.0);
                dispatch_opcode;
            }
            case 20: {
                PyrSlot* vars = g->frame->vars;
                if (slotRawFloat(&vars[2]) + 0.5 >= 0.0) {
                    slotCopy(++sp, &vars[1]); // push function
                    slotCopy(++sp, &vars[2]); // push i
                    slotCopy(++sp, &vars[3]); // push j
                    // SendSpecialMsg value
                    numArgsPushed = 3;
                    selector = gSpecialSelectors[opmValue];
                    slot = sp - 2;

                    goto class_lookup;
                } else {
                    slotCopy(++sp, &g->receiver);
                    g->sp = sp;
                    g->ip = ip;
                    returnFromMethod(g);
                    sp = g->sp;
                    ip = g->ip;
                }
                dispatch_opcode;
            }
            case 21: {
                --sp; // Drop
                PyrSlot* vars = g->frame->vars;
                SetRaw(&vars[2], slotRawFloat(&vars[2]) - 1.0); // dec i
                SetRaw(&vars[3], slotRawFloat(&vars[3]) - 1.0); // inc j
                ip -= 4;
                dispatch_opcode;
            }
            case 22: // ? question mark method
                --sp;
                if (IsNil(sp)) {
                    *sp = *(sp + 1);
                }
                dispatch_opcode;
            case 23: // if not nil push this and jump. used to implement ??
                if (NotNil(sp)) {
                    int jmplen = (ip[1] << 8) | ip[2];
                    ip += jmplen + 2;
                } else {
                    --sp;
                    ip += 2;
                }
                dispatch_opcode;
            case 24: // ifNil
                if (NotNil(sp)) {
                    int jmplen = (ip[1] << 8) | ip[2];
                    ip += jmplen + 2;
                } else {
                    ip += 2;
                }
                --sp;
                dispatch_opcode;
            case 25: // ifNotNil
                if (IsNil(sp)) {
                    int jmplen = (ip[1] << 8) | ip[2];
                    ip += jmplen + 2;
                } else {
                    ip += 2;
                }
                --sp;
                dispatch_opcode;
            case 26: // ifNotNilPushNil
                if (NotNil(sp)) {
                    int jmplen = (ip[1] << 8) | ip[2];
                    ip += jmplen + 2;
                    slotCopy(sp, &gSpecialValues[svNil]);
                } else {
                    ip += 2;
                    --sp;
                }
                dispatch_opcode;
            case 27: // ifNilPushNil
                if (IsNil(sp)) {
                    int jmplen = (ip[1] << 8) | ip[2];
                    ip += jmplen + 2;
                } else {
                    ip += 2;
                    --sp;
                }
                dispatch_opcode;
            case 28: { // switch
                PyrObject* obj = slotRawObject(sp);
                op2 = 1 + arrayAtIdentityHashInPairs(obj, (sp - 1));
                sp -= 2;
                ip += slotRawInt(&obj->slots[op2]);
                dispatch_opcode;
            }
            // Number-forSeries : 143 29, 143 30, 143 31
            case 29: {
                PyrSlot* vars = g->frame->vars;
                // 0 receiver, 1 step, 2 last, 3 function, 4 i, 5 j
                if (IsInt(vars + 0) && (IsInt(vars + 1) || IsNil(vars + 1)) && (IsInt(vars + 2) || IsNil(vars + 2))) {
                    if (IsNil(vars + 1)) {
                        if (IsNil(vars + 2))
                            SetInt(vars + 2, 0x7FFFFFFF);
                        if (slotRawInt(&vars[0]) < slotRawInt(&vars[2]))
                            SetInt(vars + 1, 1);
                        else
                            SetInt(vars + 1, -1);
                    } else {
                        if (IsNil(vars + 2)) {
                            if (slotRawInt(&vars[1]) < slotRawInt(&vars[0]))
                                SetInt(vars + 2, 0x80000000);
                            else
                                SetInt(vars + 2, 0x7FFFFFFF);
                        }
                        SetInt(vars + 1, slotRawInt(&vars[1]) - slotRawInt(&vars[0]));
                    }
                    slotCopy(&vars[4], &vars[0]);
                } else {
                    if (IsInt(vars + 0)) {
                        SetFloat(&vars[4], slotRawInt(&vars[0]));
                    } else if (IsFloat(vars + 0)) {
                        SetFloat(&vars[4], slotRawFloat(&vars[0]));
                    } else {
                    bailFromNumberSeries:
                        error("Number-forSeries : first, second or last not an Integer or Float.\n");

                        slotCopy(++sp, &g->receiver);
                        numArgsPushed = 1;
                        selector = gSpecialSelectors[opmPrimitiveFailed];
                        slot = sp;

                        goto class_lookup;
                    }

                    if (IsNil(vars + 1)) {
                        if (IsNil(vars + 2))
                            SetFloat(vars + 2, kBigBigFloat);
                        else if (IsInt(vars + 2))
                            SetFloat(&vars[2], slotRawInt(&vars[2]));
                        else if (!IsFloat(vars + 2))
                            goto bailFromNumberSeries;

                        if (slotRawFloat(&vars[4]) < slotRawFloat(&vars[2]))
                            SetFloat(vars + 1, 1.);
                        else
                            SetFloat(vars + 1, -1.);
                    } else {
                        if (IsInt(vars + 1))
                            SetFloat(&vars[1], slotRawInt(&vars[1]));
                        else if (!IsFloat(vars + 1))
                            goto bailFromNumberSeries;

                        if (IsNil(vars + 2)) {
                            if (slotRawFloat(&vars[1]) < slotRawFloat(&vars[4]))
                                SetFloat(vars + 2, kSmallSmallFloat);
                            else
                                SetFloat(vars + 2, kBigBigFloat);
                        } else if (IsInt(vars + 2))
                            SetFloat(&vars[2], slotRawInt(&vars[2]));
                        else if (!IsFloat(vars + 2))
                            goto bailFromNumberSeries;
                        SetFloat(vars + 1, slotRawFloat(&vars[1]) - slotRawFloat(&vars[4]));
                    }
                }
                dispatch_opcode;
            }
            case 30: {
                PyrSlot* vars = g->frame->vars;
                int tag = GetTag(&vars[1]);
                if (tag == tagInt) {
                    if ((slotRawInt(&vars[1]) >= 0 && slotRawInt(&vars[4]) <= slotRawInt(&vars[2]))
                        || (slotRawInt(&vars[1]) < 0 && slotRawInt(&vars[4]) >= slotRawInt(&vars[2]))) {
                        slotCopy(++sp, &vars[3]); // push function
                        slotCopy(++sp, &vars[4]); // push i
                        slotCopy(++sp, &vars[5]); // push j
                        // SendSpecialMsg value
                        numArgsPushed = 3;
                        selector = gSpecialSelectors[opmValue];
                        slot = sp - 2;

                        goto class_lookup;
                    } else {
                        slotCopy(++sp, &g->receiver);
                        g->sp = sp;
                        g->ip = ip;
                        returnFromMethod(g);
                        sp = g->sp;
                        ip = g->ip;
                    }
                } else {
                    if ((slotRawFloat(&vars[1]) >= 0. && slotRawFloat(&vars[4]) <= slotRawFloat(&vars[2]))
                        || (slotRawFloat(&vars[1]) < 0. && slotRawFloat(&vars[4]) >= slotRawFloat(&vars[2]))) {
                        slotCopy(++sp, &vars[3]); // push function
                        slotCopy(++sp, &vars[4]); // push i
                        slotCopy(++sp, &vars[5]); // push j
                        // SendSpecialMsg value
                        numArgsPushed = 3;
                        selector = gSpecialSelectors[opmValue];
                        slot = sp - 2;

                        goto class_lookup;
                    } else {
                        slotCopy(++sp, &g->receiver);
                        g->sp = sp;
                        g->ip = ip;
                        returnFromMethod(g);
                        sp = g->sp;
                        ip = g->ip;
                    }
                }
                dispatch_opcode;
            }
            case 31: {
                --sp; // Drop
                PyrSlot* vars = g->frame->vars;

                int tag = GetTag(&vars[1]);
                if (tag == tagInt) {
                    SetRaw(&vars[4], slotRawInt(&vars[4]) + slotRawInt(&vars[1])); // inc i
                } else {
                    SetRaw(&vars[4], slotRawFloat(&vars[4]) + slotRawFloat(&vars[1])); // inc i
                }
                SetRaw(&vars[5], slotRawInt(&vars[5]) + 1); // inc j
                ip -= 4;
                dispatch_opcode;
            }
            }
            dispatch_opcode;
        }


        // opStoreClassVar
        case 144:
        case 145:
        case 146:
        case 147:
        case 148:
        case 149:
        case 150:
        case 151:
        case 152:
        case 153:
        case 154:
        case 155:
        case 156:
        case 157:
        case 158:
        case 159:
        handle_op_144:
        handle_op_145:
        handle_op_146:
        handle_op_147:
        handle_op_148:
        handle_op_149:
        handle_op_150:
        handle_op_151:
        handle_op_152:
        handle_op_153:
        handle_op_154:
        handle_op_155:
        handle_op_156:
        handle_op_157:
        handle_op_158:
        handle_op_159 : {
            int op2 = op1 & 15;
            int op3 = ip[1];
            ++ip; // get class var index
            slotCopy(&g->classvars->slots[(op2 << 8) | op3], sp--);
            g->gc->GCWrite(g->classvars, (sp + 1));
            dispatch_opcode;
        }

        // opSendMsg
        case 160:
        handle_op_160 : {
            // special case for this as only arg
            int op2 = ip[1];
            ++ip; // get selector index
            slotCopy(++sp, &g->receiver);
            numArgsPushed = 1;
            selector = slotRawSymbol(&slotRawObject(&g->block->selectors)->slots[op2]);
            slot = sp;

            goto class_lookup;
        }

        case 161:
        case 162:
        case 163:
        case 164:
        case 165:
        case 166:
        case 167:
        case 168:
        case 169:
        case 170:
        case 171:
        case 172:
        case 173:
        case 174:
        case 175:
        handle_op_161:
        handle_op_162:
        handle_op_163:
        handle_op_164:
        handle_op_165:
        handle_op_166:
        handle_op_167:
        handle_op_168:
        handle_op_169:
        handle_op_170:
        handle_op_171:
        handle_op_172:
        handle_op_173:
        handle_op_174:
        handle_op_175 : {
            int op2 = ip[1];
            ++ip; // get selector index
            numArgsPushed = op1 & 15;
            selector = slotRawSymbol(&slotRawObject(&g->block->selectors)->slots[op2]);
            slot = sp - numArgsPushed + 1;

            goto class_lookup;
        }

        case 176: // opcTailCallReturnFromFunction
        handle_op_176:
#if TAILCALLOPTIMIZE
            g->tailCall = 2;
#endif
            dispatch_opcode;
        // opSuperMsg
        case 177:
        handle_op_177 : {
            // special case for this as only arg
            int op2 = ip[1];
            ++ip; // get selector index
            slotCopy(++sp, &g->receiver);
            numArgsPushed = 1;
            selector = slotRawSymbol(&slotRawObject(&g->block->selectors)->slots[op2]);
            slot = sp;
            classobj = slotRawSymbol(&slotRawClass(&g->method->ownerclass)->superclass)->u.classobj;

            goto msg_lookup;
        }

        case 178:
        case 179:
        case 180:
        case 181:
        case 182:
        case 183:
        case 184:
        case 185:
        case 186:
        case 187:
        case 188:
        case 189:
        case 190:
        case 191:

        handle_op_178:
        handle_op_179:
        handle_op_180:
        handle_op_181:
        handle_op_182:
        handle_op_183:
        handle_op_184:
        handle_op_185:
        handle_op_186:
        handle_op_187:
        handle_op_188:
        handle_op_189:
        handle_op_190:
        handle_op_191 : {
            int op2 = ip[1];
            ++ip; // get selector index
            numArgsPushed = op1 & 15;
            selector = slotRawSymbol(&slotRawObject(&g->block->selectors)->slots[op2]);
            slot = sp - numArgsPushed + 1;
            classobj = slotRawSymbol(&slotRawClass(&g->method->ownerclass)->superclass)->u.classobj;

            goto msg_lookup;
        }

        // opSendSpecialMsg
        case 192:
        handle_op_192 : {
            slotCopy(++sp, &g->receiver);
            int op2 = ip[1];
            ++ip; // get selector index
            numArgsPushed = 1;
            selector = gSpecialSelectors[op2];
            slot = sp;

            goto class_lookup;
        }

        case 193:
        case 194:
        case 195:
        case 196:
        case 197:
        case 198:
        case 199:
        case 200:
        case 201:
        case 202:
        case 203:
        case 204:
        case 205:
        case 206:
        case 207:

        handle_op_193:
        handle_op_194:
        handle_op_195:
        handle_op_196:
        handle_op_197:
        handle_op_198:
        handle_op_199:
        handle_op_200:
        handle_op_201:
        handle_op_202:
        handle_op_203:
        handle_op_204:
        handle_op_205:
        handle_op_206:
        handle_op_207 : {
            int op2 = ip[1];
            ++ip; // get selector index
            numArgsPushed = op1 & 15;
            selector = gSpecialSelectors[op2];
            slot = sp - numArgsPushed + 1;

            goto class_lookup;
        }

        // opSendSpecialUnaryArithMsg
        case 208: // opNeg
        handle_op_208:
            if (IsFloat(sp)) {
                SetFloat(sp, -slotRawFloat(sp));
#if TAILCALLOPTIMIZE
                g->tailCall = 0;
#endif
            } else if (IsInt(sp)) {
                SetRaw(sp, -slotRawInt(sp));
#if TAILCALLOPTIMIZE
                g->tailCall = 0;
#endif
            } else
                handleSendSpecialUnaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;
        case 209: // opNot
        handle_op_209:
            if (IsTrue(sp)) {
                SetTagRaw(sp, tagFalse);
#if TAILCALLOPTIMIZE
                g->tailCall = 0;
#endif
            } else if (IsFalse(sp)) {
                SetTagRaw(sp, tagTrue);
#if TAILCALLOPTIMIZE
                g->tailCall = 0;
#endif
            } else
                handleSendSpecialUnaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;
        case 210: // opIsNil
        handle_op_210:
            if (IsNil(sp)) {
                SetTagRaw(sp, tagTrue);
            } else {
                slotCopy(sp, &gSpecialValues[svFalse]);
            }
#if TAILCALLOPTIMIZE
            g->tailCall = 0;
#endif
            dispatch_opcode;
        case 211: // opNotNil
        handle_op_211:
            if (NotNil(sp)) {
                slotCopy(sp, &gSpecialValues[svTrue]);
            } else {
                SetTagRaw(sp, tagFalse);
            }
#if TAILCALLOPTIMIZE
            g->tailCall = 0;
#endif
            dispatch_opcode;

        case 212:
        handle_op_212:
            handleSendSpecialUnaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;
        case 213:
        handle_op_213:
            handleSendSpecialUnaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;
        case 214:
        handle_op_214:
            handleSendSpecialUnaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;
        case 215:
        handle_op_215:
            handleSendSpecialUnaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;
        case 216:
        handle_op_216:
            handleSendSpecialUnaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;
        case 217:
        handle_op_217:
            handleSendSpecialUnaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;
        case 218:
        handle_op_218:
            handleSendSpecialUnaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;
        case 219:
        handle_op_219:
            handleSendSpecialUnaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;
        case 220:
        handle_op_220:
            handleSendSpecialUnaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;
        case 221:
        handle_op_221:
            handleSendSpecialUnaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;
        case 222:
        handle_op_222:
            handleSendSpecialUnaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;
        case 223:
        handle_op_223:
            handleSendSpecialUnaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;


        // opSendSpecialBinaryArithMsg
        case 224: // add
        handle_op_224:
            if (IsInt(sp - 1)) {
                if (IsInt(sp)) {
                    --sp;
                    SetRaw(sp, slotRawInt(sp) + slotRawInt(sp + 1));
#if TAILCALLOPTIMIZE
                    g->tailCall = 0;
#endif
                } else {
                    g->sp = sp;
                    g->ip = ip;
                    g->primitiveIndex = opAdd;
                    prAddInt(g, -1);
                    sp = g->sp;
                    ip = g->ip;
                }
            } else {
                g->sp = sp;
                g->ip = ip;
                g->primitiveIndex = opAdd;
                prAddNum(g, -1);
                sp = g->sp;
                ip = g->ip;
            }
            dispatch_opcode;
        case 225: // subtract
        handle_op_225:
            if (IsInt(sp - 1)) {
                if (IsInt(sp)) {
                    --sp;
                    SetRaw(sp, slotRawInt(sp) - slotRawInt(sp + 1));
#if TAILCALLOPTIMIZE
                    g->tailCall = 0;
#endif
                } else {
                    g->sp = sp;
                    g->ip = ip;
                    g->primitiveIndex = opSub;
                    prSubInt(g, -1);
                    sp = g->sp;
                    ip = g->ip;
                }
            } else {
                g->sp = sp;
                g->ip = ip;
                g->primitiveIndex = opSub;
                prSubNum(g, -1);
                sp = g->sp;
                ip = g->ip;
            }
            dispatch_opcode;
        case 226: // multiply
        handle_op_226:
            if (IsInt(sp - 1)) {
                if (IsInt(sp)) {
                    --sp;
                    SetRaw(sp, slotRawInt(sp) * slotRawInt(sp + 1));
#if TAILCALLOPTIMIZE
                    g->tailCall = 0;
#endif
                } else {
                    g->sp = sp;
                    g->ip = ip;
                    g->primitiveIndex = opMul;
                    prMulInt(g, -1);
                    sp = g->sp;
                    ip = g->ip;
                }
            } else {
                g->sp = sp;
                g->ip = ip;
                g->primitiveIndex = opMul;
                prMulNum(g, -1);
                sp = g->sp;
                ip = g->ip;
            }
            dispatch_opcode;

        case 227:
        handle_op_227:
            handleSendSpecialBinaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;
        case 228:
        handle_op_228:
            handleSendSpecialBinaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;
        case 229:
        handle_op_229:
            handleSendSpecialBinaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;
        case 230:
        handle_op_230:
            handleSendSpecialBinaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;
        case 231:
        handle_op_231:
            handleSendSpecialBinaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;
        case 232:
        handle_op_232:
            handleSendSpecialBinaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;
        case 233:
        handle_op_233:
            handleSendSpecialBinaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;
        case 234:
        handle_op_234:
            handleSendSpecialBinaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;
        case 235:
        handle_op_235:
            handleSendSpecialBinaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;
        case 236:
        handle_op_236:
            handleSendSpecialBinaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;
        case 237:
        handle_op_237:
            handleSendSpecialBinaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;
        case 238:
        handle_op_238:
            handleSendSpecialBinaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;
        case 239:
        handle_op_239:
            handleSendSpecialBinaryArithMsg(g, sp, ip, op1);
            dispatch_opcode;

        // opSpecialOpcodes
        case 240:
        handle_op_240:
            --sp;
            dispatch_opcode; // opDrop
        case 241:
        handle_op_241:
            ++sp;
            *sp = sp[-1];
            dispatch_opcode; // opDup
        case 242: // opcFunctionReturn
        handle_op_242:
            g->sp = sp;
            g->ip = ip;
            returnFromBlock(g);
            sp = g->sp;
            ip = g->ip;
            dispatch_opcode;
        case 243: // opcReturn
        handle_op_243:
            g->sp = sp;
            g->ip = ip;
            returnFromMethod(g);
            sp = g->sp;
            ip = g->ip;
            dispatch_opcode;
        case 244: // opcReturnSelf
        handle_op_244:
            slotCopy(++sp, &g->receiver);
            g->sp = sp;
            g->ip = ip;
            returnFromMethod(g);
            sp = g->sp;
            ip = g->ip;
            dispatch_opcode;
        case 245: // opcReturnTrue
        handle_op_245:
            slotCopy(++sp, &gSpecialValues[svTrue]);
            g->sp = sp;
            g->ip = ip;
            returnFromMethod(g);
            sp = g->sp;
            ip = g->ip;
            dispatch_opcode;
        case 246: // opcReturnFalse
        handle_op_246:
            slotCopy(++sp, &gSpecialValues[svFalse]);
            g->sp = sp;
            g->ip = ip;
            returnFromMethod(g);
            sp = g->sp;
            ip = g->ip;
            dispatch_opcode;
        case 247: // opcReturnNil
        handle_op_247:
            slotCopy(++sp, &gSpecialValues[svNil]);
            g->sp = sp;
            g->ip = ip;
            returnFromMethod(g);
            sp = g->sp;
            ip = g->ip;
            dispatch_opcode;

        case 248: // opcJumpIfFalse
        handle_op_248:
            // cannot compare with o_false because it is NaN
            if (IsFalse(sp)) {
                int jmplen = (ip[1] << 8) | ip[2];
                ip += jmplen + 2;
            } else if (IsTrue(sp)) {
                ip += 2;
            } else {
                numArgsPushed = 1;
                selector = gSpecialSelectors[opmNonBooleanError];
                slot = sp;

                goto class_lookup;
            }
            --sp;
            dispatch_opcode;
        case 249: // opcJumpIfFalsePushNil
        handle_op_249:
            if (IsFalse(sp)) {
                int jmplen = (ip[1] << 8) | ip[2];
                ip += jmplen + 2;
                slotCopy(sp, &gSpecialValues[svNil]);
            } else if (IsTrue(sp)) {
                --sp;
                ip += 2;
            } else {
                numArgsPushed = 1;
                selector = gSpecialSelectors[opmNonBooleanError];
                slot = sp;

                goto class_lookup;
            }
            dispatch_opcode;
        case 250: // opcJumpIfFalsePushFalse
        handle_op_250:
            if (IsFalse(sp)) {
                int jmplen = (ip[1] << 8) | ip[2];
                ip += jmplen + 2;
                //*sp = r_false;
            } else if (IsTrue(sp)) {
                --sp;
                ip += 2;
            } else {
                numArgsPushed = 1;
                selector = gSpecialSelectors[opmNonBooleanError];
                slot = sp;

                goto class_lookup;
            }
            dispatch_opcode;
        case 251: // opcJumpIfTruePushTrue
        handle_op_251:
            if (IsFalse(sp)) {
                --sp;
                ip += 2;
            } else if (IsTrue(sp)) {
                int jmplen = (ip[1] << 8) | ip[2];
                ip += jmplen + 2;
                slotCopy(sp, &gSpecialValues[svTrue]);
            } else {
                numArgsPushed = 1;
                selector = gSpecialSelectors[opmNonBooleanError];
                slot = sp;

                goto class_lookup;
            }
            dispatch_opcode;
        case 252: // opcJumpFwd
        handle_op_252 : {
            int jmplen = (ip[1] << 8) | ip[2];
            ip += jmplen + 2;
            dispatch_opcode;
        }
        case 253: // opcJumpBak
        handle_op_253 : {
            --sp; // also drops the stack. This saves an opcode in the while loop
                  // which is the only place this opcode is used.
            int jmplen = (ip[1] << 8) | ip[2];
            ip -= jmplen;

            // assert(g->gc->SanityCheck());
            dispatch_opcode;
        }
        case 254: // opcSpecialBinaryOpWithAdverb
        handle_op_254 : {
            int op2 = ip[1];
            ++ip; // get selector index
            g->sp = sp;
            g->ip = ip;
            g->primitiveIndex = op2;
            doSpecialBinaryArithMsg(g, 3, false);
            sp = g->sp;
            ip = g->ip;
            dispatch_opcode;
        }
        case 255: // opcTailCallReturnFromMethod
        handle_op_255:
#if TAILCALLOPTIMIZE
            g->tailCall = 1;
#endif
            dispatch_opcode;

            ////////////////////////////////////

        class_lookup:
            // normal class lookup
            classobj = classOfSlot(slot);

            // message sends handled here:
        msg_lookup : {
            size_t index = slotRawInt(&classobj->classIndex) + selector->u.index;
            PyrMethod* meth = NULL;
            if (UNLIKELY((selector->flags & sym_Class) != 0)) {
                // You have sent a message which is a class name. This is a bad thing.
                // There are two cases. It is either an illegitimate classname like
                // 1 FooBaz: 2
                // in which case selector->u.index == 0 and you get a message or it is a real one like
                // 1 Object: 2
                // in which case selector->u.index isn't pointing to a method and you get a segfault. So...
                meth = NULL;
            } else {
                meth = gRowTable[index];
            }

            // and now if meth is null, bail out just like if I don't understand it
            if (UNLIKELY(meth == NULL || (slotRawSymbol(&meth->name) != selector))) {
                g->sp = sp;
                g->ip = ip;
                doesNotUnderstand(g, selector, numArgsPushed);
                sp = g->sp;
                ip = g->ip;
            } else {
                PyrMethodRaw* methraw = METHRAW(meth);
                switch (methraw->methType) {
                case methNormal: /* normal msg send */
                    g->sp = sp;
                    g->ip = ip;
                    executeMethod(g, meth, numArgsPushed);
                    sp = g->sp;
                    ip = g->ip;
                    break;
                case methReturnSelf: /* return self */
                    sp -= numArgsPushed - 1;
                    break;
                case methReturnLiteral: /* return literal */
                    sp -= numArgsPushed - 1;
                    slotCopy(sp, &meth->selectors); /* in this case selectors is just a single value */
                    break;
                case methReturnArg: /* return an argument */
                    sp -= numArgsPushed - 1;
                    index = methraw->specialIndex; // zero is index of the first argument
                    if (index < numArgsPushed) {
                        slotCopy(sp, sp + index);
                    } else {
                        slotCopy(sp, &slotRawObject(&meth->prototypeFrame)->slots[index]);
                    }
                    break;
                case methReturnInstVar: /* return inst var */
                    sp -= numArgsPushed - 1;
                    index = methraw->specialIndex;
                    slotCopy(sp, &slotRawObject(slot)->slots[index]);
                    break;
                case methAssignInstVar: { /* assign inst var */
                    sp -= numArgsPushed - 1;
                    index = methraw->specialIndex;
                    PyrObject* obj = slotRawObject(slot);
                    if (obj->IsImmutable()) {
                        StoreToImmutableB(g, sp, ip);
                    } else {
                        if (numArgsPushed >= 2) {
                            slotCopy(&obj->slots[index], sp + 1);
                            g->gc->GCWrite(obj, sp + 1);
                        } else {
                            slotCopy(&obj->slots[index], &gSpecialValues[svNil]);
                        }
                        slotCopy(sp, slot);
                    }
                    break;
                }
                case methReturnClassVar: /* return class var */
                    sp -= numArgsPushed - 1;
                    slotCopy(sp, &g->classvars->slots[methraw->specialIndex]);
                    break;
                case methAssignClassVar: /* assign class var */
                    sp -= numArgsPushed - 1;
                    if (numArgsPushed >= 2) {
                        slotCopy(&g->classvars->slots[methraw->specialIndex], sp + 1);
                        g->gc->GCWrite(g->classvars, sp + 1);
                    } else
                        slotCopy(&g->classvars->slots[methraw->specialIndex], &gSpecialValues[svNil]);
                    slotCopy(sp, slot);
                    break;
                case methRedirect: /* send a different selector to self */
                    if (numArgsPushed < methraw->numargs) { // not enough args pushed
                        /* push default arg values */
                        PyrSlot* qslot;
                        int m, mmax;
                        qslot = slotRawObject(&meth->prototypeFrame)->slots + numArgsPushed - 1;
                        for (m = 0, mmax = methraw->numargs - numArgsPushed; m < mmax; ++m)
                            slotCopy(++sp, ++qslot);
                        numArgsPushed = methraw->numargs;
                    }
                    selector = slotRawSymbol(&meth->selectors);
                    goto msg_lookup;
                case methRedirectSuper: /* send a different selector to self */
                    if (numArgsPushed < methraw->numargs) { // not enough args pushed
                        /* push default arg values */
                        PyrSlot* qslot;
                        int m, mmax;
                        qslot = slotRawObject(&meth->prototypeFrame)->slots + numArgsPushed - 1;
                        for (m = 0, mmax = methraw->numargs - numArgsPushed; m < mmax; ++m)
                            slotCopy(++sp, ++qslot);
                        numArgsPushed = methraw->numargs;
                    }
                    selector = slotRawSymbol(&meth->selectors);
                    classobj = slotRawSymbol(&slotRawClass(&meth->ownerclass)->superclass)->u.classobj;
                    goto msg_lookup;
                case methForwardInstVar: /* forward to an instance variable */
                    if (numArgsPushed < methraw->numargs) { // not enough args pushed
                        /* push default arg values */
                        PyrSlot* qslot;
                        int m, mmax;
                        qslot = slotRawObject(&meth->prototypeFrame)->slots + numArgsPushed - 1;
                        for (m = 0, mmax = methraw->numargs - numArgsPushed; m < mmax; ++m)
                            slotCopy(++sp, ++qslot);
                        numArgsPushed = methraw->numargs;
                    }
                    selector = slotRawSymbol(&meth->selectors);
                    index = methraw->specialIndex;
                    slotCopy(slot, &slotRawObject(slot)->slots[index]);

                    classobj = classOfSlot(slot);

                    goto msg_lookup;
                case methForwardClassVar: /* forward to an instance variable */
                    if (numArgsPushed < methraw->numargs) { // not enough args pushed
                        /* push default arg values */
                        PyrSlot* qslot;
                        int m, mmax;
                        qslot = slotRawObject(&meth->prototypeFrame)->slots + numArgsPushed - 1;
                        for (m = 0, mmax = methraw->numargs - numArgsPushed; m < mmax; ++m)
                            slotCopy(++sp, ++qslot);
                        numArgsPushed = methraw->numargs;
                    }
                    selector = slotRawSymbol(&meth->selectors);
                    slotCopy(slot, &g->classvars->slots[methraw->specialIndex]);

                    classobj = classOfSlot(slot);

                    goto msg_lookup;
                case methPrimitive: /* primitive */
                    g->sp = sp;
                    g->ip = ip;
                    doPrimitive(g, meth, numArgsPushed);
                    sp = g->sp;
                    ip = g->ip;
                    break;
                } // switch (meth->methType)
            } // end handle message
#if TAILCALLOPTIMIZE
            g->tailCall = 0;
#endif
            dispatch_opcode;
        }
            ////////////////////////////////////

        key_class_lookup:
            // normal class lookup
            classobj = classOfSlot(slot);

            // message sends handled here:
        key_msg_lookup : {
            size_t index = slotRawInt(&classobj->classIndex) + selector->u.index;
            PyrMethod* meth = gRowTable[index];

            if (UNLIKELY(slotRawSymbol(&meth->name) != selector)) {
                g->sp = sp;
                g->ip = ip;
                doesNotUnderstandWithKeys(g, selector, numArgsPushed, numKeyArgsPushed);
                sp = g->sp;
                ip = g->ip;
            } else {
                PyrMethodRaw* methraw;
                methraw = METHRAW(meth);
                switch (methraw->methType) {
                case methNormal: /* normal msg send */
                    g->sp = sp;
                    g->ip = ip;
                    executeMethodWithKeys(g, meth, numArgsPushed, numKeyArgsPushed);
                    sp = g->sp;
                    ip = g->ip;
                    break;
                case methReturnSelf: /* return self */
                    sp -= numArgsPushed - 1;
                    break;
                case methReturnLiteral: /* return literal */
                    sp -= numArgsPushed - 1;
                    slotCopy(sp, &meth->selectors); /* in this case selectors is just a single value */
                    break;
                case methReturnArg: /* return an argument */
                    g->sp = sp;
                    numArgsPushed = keywordFixStack(g, meth, methraw, numArgsPushed, numKeyArgsPushed);
                    numKeyArgsPushed = 0;
                    sp = g->sp;
                    sp -= numArgsPushed - 1;
                    index = methraw->specialIndex; // zero is index of the first argument
                    if (index < numArgsPushed) {
                        slotCopy(sp, sp + index);
                    } else {
                        slotCopy(sp, &slotRawObject(&meth->prototypeFrame)->slots[index]);
                    }
                    break;
                case methReturnInstVar: /* return inst var */
                    sp -= numArgsPushed - 1;
                    index = methraw->specialIndex;
                    slotCopy(sp, &slotRawObject(slot)->slots[index]);
                    break;
                case methAssignInstVar: { /* assign inst var */
                    sp -= numArgsPushed - 1;
                    numArgsPushed -= numKeyArgsPushed << 1;
                    index = methraw->specialIndex;
                    PyrObject* obj = slotRawObject(slot);
                    if (obj->IsImmutable()) {
                        StoreToImmutableB(g, sp, ip);
                    } else {
                        if (numArgsPushed >= 2) {
                            slotCopy(&obj->slots[index], sp + 1);
                            g->gc->GCWrite(obj, sp + 1);
                        } else
                            slotCopy(&obj->slots[index], &gSpecialValues[svNil]);
                        slotCopy(sp, slot);
                    }
                    break;
                }
                case methReturnClassVar: /* return class var */
                    sp -= numArgsPushed - 1;
                    slotCopy(sp, &g->classvars->slots[methraw->specialIndex]);
                    break;
                case methAssignClassVar: /* assign class var */
                    sp -= numArgsPushed - 1;
                    if (numArgsPushed >= 2) {
                        slotCopy(&g->classvars->slots[methraw->specialIndex], sp + 1);
                        g->gc->GCWrite(g->classvars, sp + 1);
                    } else
                        slotCopy(&g->classvars->slots[methraw->specialIndex], &gSpecialValues[svNil]);
                    slotCopy(sp, slot);
                    break;
                case methRedirect: /* send a different selector to self, e.g. this.subclassResponsibility */
                    g->sp = sp;
                    numArgsPushed = keywordFixStack(g, meth, methraw, numArgsPushed, numKeyArgsPushed);
                    numKeyArgsPushed = 0;
                    sp = g->sp;
                    selector = slotRawSymbol(&meth->selectors);

                    goto msg_lookup;
                case methRedirectSuper: /* send a different selector to super */
                    g->sp = sp;
                    numArgsPushed = keywordFixStack(g, meth, methraw, numArgsPushed, numKeyArgsPushed);
                    numKeyArgsPushed = 0;
                    sp = g->sp;
                    selector = slotRawSymbol(&meth->selectors);

                    classobj = slotRawSymbol(&slotRawClass(&meth->ownerclass)->superclass)->u.classobj;

                    goto msg_lookup;
                case methForwardInstVar: /* forward to an instance variable */
                    g->sp = sp;
                    numArgsPushed = keywordFixStack(g, meth, methraw, numArgsPushed, numKeyArgsPushed);
                    numKeyArgsPushed = 0;
                    sp = g->sp;
                    selector = slotRawSymbol(&meth->selectors);
                    index = methraw->specialIndex;
                    slotCopy(slot, &slotRawObject(slot)->slots[index]);

                    classobj = classOfSlot(slot);

                    goto msg_lookup;
                case methForwardClassVar: /* forward to an instance variable */
                    g->sp = sp;
                    numArgsPushed = keywordFixStack(g, meth, methraw, numArgsPushed, numKeyArgsPushed);
                    numKeyArgsPushed = 0;
                    sp = g->sp;
                    selector = slotRawSymbol(&meth->selectors);
                    slotCopy(slot, &g->classvars->slots[methraw->specialIndex]);

                    classobj = classOfSlot(slot);

                    goto msg_lookup;
                case methPrimitive: /* primitive */
                    g->sp = sp;
                    g->ip = ip;
                    doPrimitiveWithKeys(g, meth, numArgsPushed, numKeyArgsPushed);
                    sp = g->sp;
                    ip = g->ip;
                    break;
                } // switch (meth->methType)
            } // end handle message
            numKeyArgsPushed = 0;
#if TAILCALLOPTIMIZE
            g->tailCall = 0;
#endif
            dispatch_opcode;
        }
        } // switch(op1)
    } // end while(running)
#ifndef _WIN32
    running = true; // reset the signal
#endif
    g->sp = sp;
    g->ip = ip;
}


#if defined(__GNUC__) && !defined(__clang__)
#    pragma GCC pop_options
#endif


void DumpSimpleBackTrace(VMGlobals* g);
void DumpSimpleBackTrace(VMGlobals* g) {
    int i;
    PyrFrame* frame;

    post("CALL STACK:\n");
    // print the variables and arguments for the most recent frames in the
    // call graph
    frame = g->frame;

    for (i = 0; i < 16; ++i) {
        char str[256];
        slotOneWord(&frame->method, str);
        post("%s ip %d\n", str,
             (char*)slotRawPtr(&frame->ip) - (char*)slotRawObject(&slotRawMethod(&frame->method)->code)->slots);
        frame = slotRawFrame(&frame->caller);
        if (!frame)
            break;
    }
    if (frame) {
        post("...\n");
    }
    // DumpStack(g, g->sp);
}

void DumpBackTrace(VMGlobals* g) {
    int i;
    PyrFrame* frame;

    post("CALL STACK:\n");
    // print the variables and arguments for the most recent frames in the
    // call graph
    frame = g->frame;

    for (i = 0; i < 16; ++i) {
        if (FrameSanity(frame, "DumpBackTrace")) {
            post("FRAME CORRUPTED\n");
            return;
        }
        DumpFrame(frame);
        frame = slotRawFrame(&frame->caller);
        if (!frame)
            break;
    }
    if (frame) {
        post("...\n");
    }
    // DumpStack(g, g->sp);
}

void DumpDetailedFrame(PyrFrame* frame);
void DumpDetailedBackTrace(VMGlobals* g);
void DumpDetailedBackTrace(VMGlobals* g) {
    int i;
    PyrFrame* frame;

    post("CALL STACK:\n");
    // print the variables and arguments for the most recent frames in the
    // call graph
    frame = g->frame;

    for (i = 0; i < 16; ++i) {
        if (FrameSanity(frame, "DumpDetailedBackTrace")) {
            post("FRAME CORRUPTED\n");
            return;
        }
        DumpDetailedFrame(frame);
        frame = slotRawFrame(&frame->caller);
        if (!frame)
            break;
    }
    if (frame) {
        post("...\n");
    }
    // DumpStack(g, g->sp);
}

void DumpStack(VMGlobals* g, PyrSlot* sp) {
    PyrSlot* slot;
    char str[128];
#if BCSTAT
    dumpbcstat();
#endif
    postfl("STACK:\n");
    slot = sp - 64;
    if (slot < g->gc->Stack()->slots)
        slot = g->gc->Stack()->slots;
    for (size_t i = slot - g->gc->Stack()->slots; slot <= sp; slot++, ++i) {
        slotString(slot, str);
        post("   %2d  %s\n", i, str);
    }
}
