Example #1
0
void hsh_reorganize( hshtblptr master )
{
	void*         *newtbl = NULL;
	void*         *oldtbl;
	unsigned long  newsize, oldsize;
	unsigned long  oldentries, j;
	unsigned int   i;

	oldsize = master->currentsz;
	oldtbl =  master->htbl;
	oldentries = 0;

	if (master->hstatus.hdeleted > (master->hstatus.hentries / 4))
		newsize = oldsize;
	else
	{
		newsize = ithprime(0);
		for (i = 1; newsize <= oldsize; i+=1)		
		    newsize = ithprime(i);
	}
	newtbl = maketbl(newsize);

	master->currentsz = newsize;
	master->htbl = newtbl;

	unsigned long hi;
	void         *ho;
        int_pointer_t hsh_key;

	for (j = 0; j < oldsize; j++)
	    if (oldtbl[j] && (oldtbl[j] != _DELETED))
		{
			hsh_key = _hget_key( oldtbl[j] );
			hsh_lookup( master, hsh_key, hi, ho, _hget_key);	
			tm_assert( ho == NULL );
			master->htbl[ hi ] = oldtbl[j];
			
			oldentries++;
		}
  
	/* Sanity check */
	tm_assert( oldentries == master->hstatus.hentries - master->hstatus.hdeleted );
	
	master->htbl_end = master->htbl + master->currentsz;
	master->last_found_key = 0;
	master->hstatus.hentries = oldentries;
	master->hstatus.hdeleted = 0;
	free(oldtbl);
}
Example #2
0
bool safe_strtoull(const char *str, uint64_t *out) {
    // [branch 008b] switch to safe asserts
    tm_assert(out != NULL);
    errno = 0;
    *out = 0;
    char *endptr;
    // [branch 009b] Switch to safe strtoull
    unsigned long long ull = tm_strtoull(str, &endptr, 10);
    if ((errno == ERANGE) || (str == endptr)) {
        return false;
    }

    if (xisspace(*endptr) || (*endptr == '\0' && endptr != str)) {
        if ((long long) ull < 0) {
            /* only check for negative signs in the uncommon case when
             * the unsigned number is so big that it's negative as a
             * signed number. */
            // [branch 009b] Switch to safe strchr
            if (tm_strchr(str, '-') != NULL) {
                return false;
            }
        }
        *out = ull;
        return true;
    }
    return false;
}
Example #3
0
/**
 * mgr_on_abort comes here
 *
 * reason codes:
 * 0: memory conflict
 * 1: validation failure in hybrid hardware commit phase
 * 2: validation failure in hybrid software commit phase
 */
void mgr_on_abort( int reason )
{
    tm_assert(__trap_access);
    read_set_reset( &p_trans.read_set );
    write_set_reset( &p_trans.write_set );
    __hybrid_aborted = 1;
    // TODO: reason 1: hw validation abort, 2. sw validation abort
    longjmp( p_trans.jbuf, 1 );
}
Example #4
0
jmp_buf* mgr_on_begin()
{
    tm_assert(__trap_access);

    read_set_init( &p_trans.read_set );
    write_set_init( &p_trans.write_set );

    return &(p_trans.jbuf);
}
Example #5
0
static PREFIX_STATS *stats_prefix_find(const char *key, const size_t nkey) {
    PREFIX_STATS *pfs;
    uint32_t hashval;
    size_t length;
    bool bailout = true;

    // [branch 008] Switch to safe assertions
    tm_assert(key != NULL);

    for (length = 0; length < nkey && key[length] != '\0'; length++) {
        if (key[length] == settings.prefix_delimiter) {
            bailout = false;
            break;
        }
    }

    if (bailout) {
        return NULL;
    }

    hashval = hash(key, length, 0) % PREFIX_HASH_SIZE;

    for (pfs = prefix_stats[hashval]; NULL != pfs; pfs = pfs->next) {
        // [branch 009] Switch to tm-safe strncmp
        if (tm_strncmp(pfs->prefix, key, length) == 0)
            return pfs;
    }

    pfs = calloc(sizeof(PREFIX_STATS), 1);
    if (NULL == pfs) {
        // [branch 012] move perror to oncommit
        registerOnCommitHandler(spf_perror1, (void*)(uintptr_t)errno);
        return NULL;
    }

    pfs->prefix = malloc(length + 1);
    if (NULL == pfs->prefix) {
        // [branch 012] move perror to oncommit
        registerOnCommitHandler(spf_perror2, (void*)(uintptr_t)errno);
        free(pfs);
        return NULL;
    }

    // [branch 009] Switch to safe strncpy
    tm_strncpy(pfs->prefix, key, length);
    pfs->prefix[length] = '\0';      /* because strncpy() sucks */
    pfs->prefix_len = length;

    pfs->next = prefix_stats[hashval];
    prefix_stats[hashval] = pfs;

    num_prefixes++;
    total_prefix_size += length;

    return pfs;
}
Example #6
0
/*@null@*/
char *stats_prefix_dump(int *length) {
    const char *format = "PREFIX %s get %llu hit %llu set %llu del %llu\r\n";
    PREFIX_STATS *pfs;
    char *buf;
    int i, pos;
    size_t size = 0, written = 0, total_written = 0;

    /*
     * Figure out how big the buffer needs to be. This is the sum of the
     * lengths of the prefixes themselves, plus the size of one copy of
     * the per-prefix output with 20-digit values for all the counts,
     * plus space for the "END" at the end.
     */
    // [branch 002] Replaced STATS_LOCK with relaxed transaction
    // [branch 012] With oncommit, this becomes atomic
    __transaction_atomic {
        // [branch 009] Switched to safe strlen functions
    size = tm_strlen(format) + total_prefix_size +
           num_prefixes * (tm_strlen(format) - 2 /* %s */
                           + 4 * (20 - 4)) /* %llu replaced by 20-digit num */
                           + sizeof("END\r\n");
    buf = malloc(size);
    if (NULL == buf) {
        // [branch 012] Move perror to oncommit handler
        registerOnCommitHandler(spd_perror1, (void*)(uintptr_t)errno);
        return NULL;
    }

    pos = 0;
    for (i = 0; i < PREFIX_HASH_SIZE; i++) {
        for (pfs = prefix_stats[i]; NULL != pfs; pfs = pfs->next) {
            // [branch 011] Marhsall pfs->prefix into a local, so we can use
            //              a pure snprintf variant
            char local[4096];
            tm_strncpy_to_local(local, pfs->prefix, 4096);
            written = tm_snprintf_s_llu_llu_llu_llu(buf + pos, size-pos, format,
                               local, // pfs->prefix,
                               pfs->num_gets, pfs->num_hits,
                               pfs->num_sets, pfs->num_deletes);
            pos += written;
            total_written += written;
            // [branch 008] Switch to safe assertions
            tm_assert(total_written < size);
        }
    }

    }
    memcpy(buf + pos, "END\r\n", 6);

    *length = pos + 5;
    return buf;
}
Example #7
0
/* return 0 for i value out of range                    */
static unsigned long ithprime(size_t i)
{
	tm_assert( (i < (sizeof primetbl / sizeof (int))) && primetbl[i] );
	return ((1 << (FIRSTN + i)) - primetbl[i]);
}
Example #8
0
/**
 * API: Initialize the tm allocator.
 *
 * This should be called from main():
 *
 * <pre>
 * int main(int argc, char **argv, char **envp)
 * {
 *   tm_init(&argc, &argv, &envp);
 *   ...
 * }
 * </pre>
 */
void tm_init(int *argcp, char ***argvp, char ***envpp)
{
  int i;

  tm_assert(sizeof(tm_ptr_word) == sizeof(void *));

  /*! Initialize allocation log. */
  tm_alloc_log_init();

  /*! Initialize colors. */
  tm_colors_init(&tm.colors);

#if 0
  /*! Initialize allocation colors. */
  tm.alloc_color = tm_ECRU;

  /*! Initialize phase data. */
  tm_phase_data_init(&tm.p);
#endif

  /*! Initialize time stat names. */
  tm.ts_os_alloc.name = "tm_os_alloc";
  tm.ts_os_free.name = "tm_os_free";
  tm.ts_alloc.name = "tm_alloc";
  tm.ts_free.name = "tm_free";
  tm.ts_gc.name = "gc";
  tm.ts_gc_inner.name = "gc_inner";
  tm.ts_barrier.name = "tm_barrier";
  tm.ts_barrier_pure.name = "tm_barrier_p";
  tm.ts_barrier_root.name = "tm_barrier_r";
  tm.ts_barrier_black.name = "tm_barrier B";

  /*! Initialize tm_msg(). */
  tm_msg_init();

  tm_list_assert_layout();

  /*! Warn if already initalized. */
  if ( tm.inited ) {
    tm_msg("WARNING: tm_init() called more than once.\nf");
  }
  tm.initing ++;

  /*! Error if argcp and argvp are not given. */
  if ( ! argcp || ! argvp ) {
    tm_msg("WARNING: tm_init() not called from main().\n");
  }

  /*! Default envpp = &environ, if not given. */
  if ( ! envpp ) {
    extern char **environ;
    tm_msg("WARNING: tm_init(): not passed &envp.\n");
    envpp = (char ***) &environ;
  }

#if 0
#define P(X) tm_msg(" %s = %ld\n", #X, (long) X);
  P(sizeof(tm_list));
  P(sizeof(tm_node));
  P(sizeof(tm_block));
  P(sizeof(tm_type));
  P(sizeof(struct tm_data));
#undef P
#endif

  /*! Initialize possible pointer range. */
  tm_ptr_l = (void*) ~0UL;
  tm_ptr_h = 0;

  /*! Initialize root sets. */
  tm.nroots = 0;
  tm.data_mutations = tm.stack_mutations = 0;

  tm.root_datai = -1;

  /*! Initialize root set for the register set, using a jmpbuf struct. */
  /* A C jmpbuf struct contains the saved registers set, hopefully. */
  tm_root_add("register", &tm.jb, (&tm.jb) + 1);

  /*! Initialize roots set for the stack. */

  {
    void *bottom_of_stack, *top_of_stack = (void*) &i;

#ifndef tm_ENVIRON_0_ALLOCATED_ON_STACK
#define tm_ENVIRON_0_ALLOCATED_ON_STACK 1
#endif

#if tm_ENVIRON_0_ALLOCATED_ON_STACK
    {
      extern char **environ;
      bottom_of_stack = (void*) environ[0];
    }
#else
  /* argvp contains a caller's auto variable. */
  /* Hope that we are being called from somewhere close to the bottom of the stack. */
    bottom_of_stack = (void*) argvp;
#endif

    {
      void *l = top_of_stack;
      void *h = bottom_of_stack;
      
      /* Determine direction of stack growth. */
      if ( tm_stack_growth((char*) &l, 5) < 0 ) {
	tm.stack_grows = -1;
      } else {
	void *t = l;
	l = h;
	h = t;
	tm.stack_grows = 1;
      }
      
      /* Attempt to nudge to page boundaries. */
      {
	size_t stack_page_size = 4096;
	l = (void*) ((tm_ptr_word) (l) - ((tm_ptr_word) (l) % stack_page_size));
	h = (void*) ((tm_ptr_word) (h) + (stack_page_size - ((tm_ptr_word) (h) % stack_page_size)));
      }

      i = tm_root_add("stack", l, h);
    }

    /* Remember where to put the stack pointer. */
    if ( tm.stack_grows < 0 ) {
      tm.stack_ptrp = (void**) &tm.roots[i].l;
    } else {
      tm.stack_ptrp = (void**) &tm.roots[i].h;
    }

    tm.root_datai = i + 1;

    /* IMPLEMENT: Support for multithreading stacks. */
  }


  /*! Remove anti-roots: do not scan tm's internal data structures. */
  tm_root_remove("tm", &tm, &tm + 1);

  /*! Initialize root set for initialized and uninitialize (zeroed) data segments. */
#ifdef __win32__
  {
    extern int _data_start__, _data_end__, _bss_start__, _bss_end__;

    tm_root_add("initialized data", &_data_start__, &_data_end__);
    tm_root_add("uninitialized data", &_bss_start__, &_bss_end__);

  }
#define tm_roots_data_segs
#endif

#ifdef __linux__
  {
    extern int __data_start, __bss_start, _end;

#if 0
    fprintf(stderr, "__data_start = %d\n", __data_start);
    fprintf(stderr, "__bss_start = %d\n", __bss_start);
    fprintf(stderr, "_end = %d\n", _end);
#endif

#if 1
    tm_assert(&__data_start < &__bss_start && &__bss_start < &_end);
    tm_root_add("initialized data", &__data_start, &__bss_start);
    tm_root_add("uninitialized data", &__bss_start, &_end);
#endif
  }
#define tm_roots_data_segs
#endif

#ifndef tm_roots_data_segs
#error must specify how to find the data segment(s) for root marking.
#endif


  /*! IMPLEMENT: Support dynamically-loaded library data segments. */

  /*! Dump the tm_root sets. */
  tm_msg_enable("R", 1);

  tm_msg("R ROOTS {\n");
  for ( i = 0; tm.roots[i].name; ++ i ) {
    if ( tm.roots[i].l != tm.roots[i].h ) {
      tm_msg("R \t [%p,%p] %s %d\n",
	     tm.roots[i].l,
	     tm.roots[i].h,
	     tm.roots[i].name,
	     i);
    }
  }
  tm_msg("R }\n");

  tm_msg_enable("R", 0);

  /*! Validate tm_root sets. */
  {
    extern int _tm_user_bss[], _tm_user_data[];

    tm_assert(tm_ptr_is_in_root_set(_tm_user_bss),  ": _tm_user_bss = %p", _tm_user_bss);
    tm_assert(tm_ptr_is_in_root_set(_tm_user_data), ": _tm_user_data = %p", _tm_user_data);
    _tm_set_stack_ptr(&i);
    tm_assert(tm_ptr_is_in_root_set(&i), ": &i = %p", &i);
  }

  /*! Initialize root marking loop. */
  _tm_root_loop_init();

  /*! Initialize global tm_type list. */
  tm_list_init(&tm.types);
  tm_list_set_color(&tm.types, tm_LIVE_TYPE);

#if 0
  /*! Initialize tm_node color iterators. */
#define X(C) \
  tm.node_color_iter[C].color = C; \
  tm_node_LOOP_INIT(C)
  
  X(tm_WHITE);
  X(tm_ECRU);
  X(tm_GREY);
  X(tm_BLACK);

#undef X
#endif

  /*! Initialize page managment. */
  memset(tm.page_in_use, 0, sizeof(tm.page_in_use));

  /*! Initialize tm_block free list. */
  tm_list_init(&tm.free_blocks);
  tm_list_set_color(&tm.free_blocks, tm_FREE_BLOCK);
  tm.free_blocks_n = 0;

  /* Types. */

  /*! Initialize tm_type free list. */
  for ( i = 0; i < sizeof(tm.type_reserve)/sizeof(tm.type_reserve[0]); ++ i ) {
    tm_type *t = &tm.type_reserve[i];
    t->hash_next = (void*) tm.type_free;
    tm.type_free = t;
  }
  
  /*! Initialize size to tm_type hash table. */
  for ( i = 0; i < tm_type_hash_LEN; i ++ ) {
    tm.type_hash[i] = 0;
  }

  /*! Initialize block sweep iterator. */
  _tm_block_sweep_init();

#if 0
  /*! Initialize phase: start by unmarking. */
  _tm_phase_init(tm_UNMARK);
#endif

  /*! Set up write barrier hooks. */
  _tm_write_barrier = __tm_write_barrier;
  _tm_write_barrier_pure = __tm_write_barrier_pure;
  _tm_write_barrier_root = __tm_write_barrier_root;
  
  /*! Mark system as initialized. */
  -- tm.initing;
  ++ tm.inited;
  tm_msg_enable("WF", 0);
}
Example #9
0
/** 
** evaluate byte code.
** @param f: Frame
** @return evaluated value.
*/
Object tm_eval(TmFrame* f) {
    Object* locals    = f->locals;
    Object* top       = f->stack;
    Object cur_fnc    = f->fnc;
    Object globals    = get_globals(cur_fnc);
    // TODO use code cache to replace unsigned char*
    unsigned char* pc = f->pc;
    const char* func_name_sz = get_func_name_sz(cur_fnc);

    Object x, k, v;
    Object ret = NONE_OBJECT;
    int i;

    #if INTERP_DB
        printf("File \"%s\": enter function %s\n",get_func_file_sz(cur_fnc), get_func_name_sz(cur_fnc));
    #endif
    while (1) {
        i = (pc[1] << 8) | pc[2];
        
        #if INTERP_DB
            printf("%30s%2d: %d frame = %d, top = %d\n","", pc[0], i, tm->cur, (int) (top - f->stack));
        #endif    
        switch (pc[0]) {

        case OP_NUMBER: {
            double d = atof((char*)pc + 3);
            pc += i;
            v = tm_number(d);
            /* obj_append(tm->constants,v);*/
            dict_set(tm->constants, v, NONE_OBJECT);
            break;
        }

        case OP_STRING: {
            v = string_alloc((char*)pc + 3, i);
            pc += i;
            /* obj_append(tm->constants,v); */
            dict_set(tm->constants, v, NONE_OBJECT);
            break;
        }

        case OP_IMPORT: {
            // TODO
            // tm_import(globals)
            Object import_func = tm_get_global(globals, "_import");
            arg_start();
            arg_push(globals);
            Object modname, attr;
            
            if (i == 1) {
                modname = TM_POP();
                arg_push(modname); // arg1
            } else {
                attr = TM_POP();
                modname = TM_POP();
                arg_push(modname);
                arg_push(attr);
            }
            call_function(import_func);
            break;
        }
        case OP_CONSTANT: {
            TM_PUSH(GET_CONST(i));
            break;
        }
        
        case OP_NONE: {
            TM_PUSH(NONE_OBJECT);
            break;
        }

        case OP_LOAD_LOCAL: {
            TM_PUSH(locals[i]);
            break;
        }

        case OP_STORE_LOCAL:
            locals[i] = TM_POP();
            break;

        case OP_LOAD_GLOBAL: {
            /* tm_printf("load global %o\n", GET_CONST(i)); */
            int idx = dict_get_attr(GET_DICT(globals), i);
            if (idx == -1) {
                idx = dict_get_attr(GET_DICT(tm->builtins), i);
                if (idx == -1) {
                    tm_raise("NameError: name %o is not defined", GET_CONST(i));
                } else {
                    Object value = GET_DICT(tm->builtins)->nodes[idx].val;
                    // OPTIMIZE
                    // set the builtin to `globals()`
                    obj_set(globals, GET_CONST(i), value);
                    idx = dict_get_attr(GET_DICT(globals), i);
                    pc[0] = OP_FAST_LD_GLO;
                    code16(pc+1, idx);
                    // OPTIMIZE END
                    TM_PUSH(value);
                }
            } else {
                TM_PUSH(GET_DICT(globals)->nodes[idx].val);
                pc[0] = OP_FAST_LD_GLO;
                code16(pc+1, idx);
            }
            break;
        }
        case OP_STORE_GLOBAL: {
            x = TM_POP();
            int idx = dict_set_attr(GET_DICT(globals), i, x);
            pc[0] = OP_FAST_ST_GLO;
            code16(pc+1, idx);
            break;
        }
        case OP_FAST_LD_GLO: {
            TM_PUSH(GET_DICT(globals)->nodes[i].val);
            break;
        }
        case OP_FAST_ST_GLO: {
            GET_DICT(globals)->nodes[i].val = TM_POP();
            break;
        }
        case OP_LIST: {
            TM_PUSH(list_new(2));
            FRAME_CHECK_GC();
            break;
        }
        case OP_APPEND:
            v = TM_POP();
            x = TM_TOP();
            tm_assert(IS_LIST(x), "tm_eval: OP_APPEND require list");
            list_append(GET_LIST(x), v);
            break;
        case OP_DICT_SET:
            v = TM_POP();
            k = TM_POP();
            x = TM_TOP();
            tm_assert(IS_DICT(x), "tm_eval: OP_DICT_SET require dict");
            obj_set(x, k, v);
            break;
        case OP_DICT: {
            TM_PUSH(dict_new());
            FRAME_CHECK_GC();
            break;
        }
        TM_OP(OP_ADD, obj_add)
        TM_OP(OP_SUB, obj_sub)
        TM_OP(OP_MUL, obj_mul)
        TM_OP(OP_DIV, obj_div)
        TM_OP(OP_MOD, obj_mod)
        TM_OP(OP_GET, obj_get)
        case OP_SLICE: {
            Object second = TM_POP();
            Object first = TM_POP();
            *top = obj_slice(*top, first, second);
            break;
        }
        case OP_EQEQ: { *(top-1) = tm_number(obj_equals(*(top-1), *top)); top--; break; }
        case OP_NOTEQ: { *(top-1) = tm_number(!obj_equals(*(top-1), *top)); top--; break; }
        case OP_LT: {
            *(top-1) = tm_number(obj_cmp(*(top-1), *top)<0);
            top--;
            break;
        }
        case OP_LTEQ: {
            *(top-1) = tm_number(obj_cmp(*(top-1), *top)<=0);
            top--;
            break;
        }
        case OP_GT: {
            *(top-1) = tm_number(obj_cmp(*(top-1), *top)>0);
            top--;
            break;
        }
        case OP_GTEQ: {
            *(top-1) = tm_number(obj_cmp(*(top-1), *top)>=0);
            top--;
            break;
        }
        case OP_IN: {
            *(top-1) = tm_number(obj_in(*(top-1), *top));
            top--;
            break;
        }
        case OP_AND: {
            *(top-1) = tm_number(is_true_obj(*(top-1)) && is_true_obj(*top));
            top--;
            break;
        }
        case OP_OR: {
            *(top-1) = tm_number(is_true_obj(*(top-1)) || is_true_obj(*top));
            top--;
            break;
        }
        case OP_NOT:{
            *top = tm_number(!is_true_obj(*top));
            break;
        }
        case OP_SET:
            k = TM_POP();
            x = TM_POP();
            v = TM_POP();
            #if INTERP_DB
                tm_printf("Self %o, Key %o, Val %o\n", x, k, v);
            #endif
            obj_set(x, k, v);
            break;
        case OP_POP: {
            top--;
            break;
        }
        case OP_NEG:
            TM_TOP() = obj_neg(TM_TOP());
            break;
        case OP_CALL: {
            f->top = top;
            top -= i;
            arg_set_arguments(top + 1, i);
            Object func = TM_POP();
            
            TM_PUSH(call_function(func));
            // TM_PUSH(call_function(func));
            tm->frame = f;
            FRAME_CHECK_GC();
            break;
        }
        
        case OP_APPLY: {
            f->top = top;
            Object args = TM_POP();
            tm_assert_type(args, TYPE_LIST, "tm_eval: OP_APPLY");
            arg_set_arguments(LIST_NODES(args), LIST_LEN(args));
            Object func = TM_POP();
            x = call_function(func);
            TM_PUSH(x);
            tm->frame = f;
            FRAME_CHECK_GC();
            break;
        }
        case OP_LOAD_PARAMS: {
            int parg = pc[1];
            int varg = pc[2];
            if (tm->arg_cnt < parg || tm->arg_cnt > parg + varg) {
                tm_raise("ArgError,parg=%d,varg=%d,given=%d", 
                    parg, varg, tm->arg_cnt);
            }
            for(i = 0; i < tm->arg_cnt; i++){
                locals[i] = tm->arguments[i];
            }
            break;
        }
        case OP_LOAD_PARG: {
            int parg = i;
            for (i = 0; i < parg; i++) {
                locals[i] = arg_take_obj(func_name_sz);
            }
            break;
        }
        case OP_LOAD_NARG: {
            int arg_index = i;
            Object list = list_new(tm->arg_cnt);
            while (arg_remains() > 0) {
                obj_append(list, arg_take_obj(func_name_sz));
            }
            locals[arg_index] = list;
            break;
        }
        case OP_ITER: {
            *top = iter_new(*top);
            break;
        }
        case OP_NEXT: {
            Object *next = next_ptr(*top);
            if (next != NULL) {
                TM_PUSH(*next);
                break;
            } else {
                pc += i * 3;
                continue;
            }
            break;
        }
        case OP_DEF: {
            Object mod = GET_FUNCTION(cur_fnc)->mod;
            Object fnc = func_new(mod, NONE_OBJECT, NULL);
            pc = func_resolve(GET_FUNCTION(fnc), pc);
            GET_FUNCTION_NAME(fnc) = GET_CONST(i);
            TM_PUSH(fnc);
            continue;
        }
        case OP_RETURN: {
            ret = TM_POP();
            goto end;
        }
        case OP_ROT: {
            int half = i / 2;
            int j;
            for (j = 0; j < half; j++) {
                Object temp = *(top - j);
                *(top-j) = *(top - i + j + 1);
                *(top-i+j+1) = temp;
            }
            break;
        }
        case OP_UNPACK: {
            x = TM_POP();
            tm_assert_type(x, TYPE_LIST, "tm_eval:UNPACK");
            int j;
            for(j = LIST_LEN(x)-1; j >= 0; j--) {
                TM_PUSH(LIST_GET(x, j));
            }
            break;
        }

        case OP_DEL: {
            k = TM_POP();
            x = TM_POP();
            obj_del(x, k);
            break;
        }

        case OP_POP_JUMP_ON_FALSE: {
            if (!is_true_obj(TM_POP())) {
                pc += i * 3;
                continue;
            }
            break;
        }

        case OP_JUMP_ON_TRUE: {
            if (is_true_obj(TM_TOP())) {
                pc += i * 3;
                continue;
            }
            break;
        }

        case OP_JUMP_ON_FALSE: {
            if (!is_true_obj(TM_TOP())) {
                pc += i * 3;
                continue;
            }
            break;
        }

        case OP_UP_JUMP:
            pc -= i * 3;
            continue;

        case OP_JUMP:
            pc += i * 3;
            continue;

        case OP_EOP:
        case OP_EOF: {
           ret = NONE_OBJECT;
           goto end;
        }

        case OP_LOAD_EX: { top = f->last_top; TM_PUSH(tm->ex); break; }
        case OP_SETJUMP: { f->last_top = top; f->jmp = pc + i * 3; break; }
        case OP_CLR_JUMP: { f->jmp = NULL; break;}
        case OP_LINE: { f->lineno = i; break;}

        case OP_DEBUG: {
            #if 0
            Object fdebug = tm_get_global(globals, "__debug__");
            f->top = top;
            tm_call(0, fdebug, 1, tm_number(tm->frame - tm->frames));        
            break;
            #endif
        }
        
        case OP_FILE: {
            // module name here.
            break;
        }

        default:
            tm_raise("BAD INSTRUCTION, %d\n  globals() = \n%o", pc[0],
                    GET_FUNCTION_GLOBALS(f->fnc));
            goto end;
        }

        pc += 3;
    }

    end:
    /*
    if (top != f->stack) {
        tm_raise("tm_eval: operand stack overflow");
    }*/
    pop_frame();
    return ret;
}