text: /*
  ** JIT library.
  ** Copyright (C) 2005-2020 Mike Pall. See Copyright Notice in luajit.h
  */
  
  #define lib_jit_c
  #define LUA_LIB
  
  #include "lua.h"
  #include "lauxlib.h"
  #include "lualib.h"
  
  #include "lj_obj.h"
  #include "lj_gc.h"
  #include "lj_err.h"
  #include "lj_debug.h"
  #include "lj_str.h"
  #include "lj_tab.h"
  #include "lj_state.h"
  #include "lj_bc.h"
  #if LJ_HASFFI
  #include "lj_ctype.h"
  #endif
  #if LJ_HASJIT
  #include "lj_ir.h"
  #include "lj_jit.h"
  #include "lj_ircall.h"
  #include "lj_iropt.h"
  #include "lj_target.h"
  #endif
  #include "lj_trace.h"
  #include "lj_dispatch.h"
  #include "lj_vm.h"
  #include "lj_vmevent.h"
  #include "lj_lib.h"
  
  #include "luajit.h"
  
  /* -- jit.* functions ----------------------------------------------------- */
  
  #define LJLIB_MODULE_jit
  
  static int setjitmode(lua_State *L, int mode)
  {
    int idx = 0;
    if (L->base == L->top || tvisnil(L->base)) {  /* jit.on/off/flush([nil]) */
      mode |= LUAJIT_MODE_ENGINE;
    } else {
      /* jit.on/off/flush(func|proto, nil|true|false) */
      if (tvisfunc(L->base) || tvisproto(L->base))
        idx = 1;
      else if (!tvistrue(L->base))  /* jit.on/off/flush(true, nil|true|false) */
        goto err;
      if (L->base+1 < L->top && tvisbool(L->base+1))
        mode |= boolV(L->base+1) ? LUAJIT_MODE_ALLFUNC : LUAJIT_MODE_ALLSUBFUNC;
      else
        mode |= LUAJIT_MODE_FUNC;
    }
    if (luaJIT_setmode(L, idx, mode) != 1) {
      if ((mode & LUAJIT_MODE_MASK) == LUAJIT_MODE_ENGINE)
        lj_err_caller(L, LJ_ERR_NOJIT);
    err:
      lj_err_argt(L, 1, LUA_TFUNCTION);
    }
    return 0;
  }
  
  LJLIB_CF(jit_on)
  {
    return setjitmode(L, LUAJIT_MODE_ON);
  }
  
  LJLIB_CF(jit_off)
  {
    return setjitmode(L, LUAJIT_MODE_OFF);
  }
  
  LJLIB_CF(jit_flush)
  {
  #if LJ_HASJIT
    if (L->base < L->top && tvisnumber(L->base)) {
      int traceno = lj_lib_checkint(L, 1);
      luaJIT_setmode(L, traceno, LUAJIT_MODE_FLUSH|LUAJIT_MODE_TRACE);
      return 0;
    }
  #endif
    return setjitmode(L, LUAJIT_MODE_FLUSH);
  }
  
  #if LJ_HASJIT
  /* Push a string for every flag bit that is set. */
  static void flagbits_to_strings(lua_State *L, uint32_t flags, uint32_t base,
  				const char *str)
  {
    for (; *str; base <<= 1, str += 1+*str)
      if (flags & base)
        setstrV(L, L->top++, lj_str_new(L, str+1, *(uint8_t *)str));
  }
  #endif
  
  LJLIB_CF(jit_status)
  {
  #if LJ_HASJIT
    jit_State *J = L2J(L);
    L->top = L->base;
    setboolV(L->top++, (J->flags & JIT_F_ON) ? 1 : 0);
    flagbits_to_strings(L, J->flags, JIT_F_CPU, JIT_F_CPUSTRING);
    flagbits_to_strings(L, J->flags, JIT_F_OPT, JIT_F_OPTSTRING);
    return (int)(L->top - L->base);
  #else
    setboolV(L->top++, 0);
    return 1;
  #endif
  }
  
  LJLIB_CF(jit_security)
  {
    int idx = lj_lib_checkopt(L, 1, -1, LJ_SECURITY_MODESTRING);
    setintV(L->top++, ((LJ_SECURITY_MODE >> (2*idx)) & 3));
    return 1;
  }
  
  LJLIB_CF(jit_attach)
  {
  #ifdef LUAJIT_DISABLE_VMEVENT
    luaL_error(L, "vmevent API disabled");
  #else
    GCfunc *fn = lj_lib_checkfunc(L, 1);
    GCstr *s = lj_lib_optstr(L, 2);
    luaL_findtable(L, LUA_REGISTRYINDEX, LJ_VMEVENTS_REGKEY, LJ_VMEVENTS_HSIZE);
    if (s) {  /* Attach to given event. */
      const uint8_t *p = (const uint8_t *)strdata(s);
      uint32_t h = s->len;
      while (*p) h = h ^ (lj_rol(h, 6) + *p++);
      lua_pushvalue(L, 1);
      lua_rawseti(L, -2, VMEVENT_HASHIDX(h));
      G(L)->vmevmask = VMEVENT_NOCACHE;  /* Invalidate cache. */
    } else {  /* Detach if no event given. */
      setnilV(L->top++);
      while (lua_next(L, -2)) {
        L->top--;
        if (tvisfunc(L->top) && funcV(L->top) == fn) {
  	setnilV(lj_tab_set(L, tabV(L->top-2), L->top-1));
        }
      }
    }
  #endif
    return 0;
  }
  
  LJLIB_CF(jit_prngstate)
  {
    int i;
    GCtab *cur = lj_tab_new(L, 8, 0);
  
  #if LJ_HASJIT
    jit_State *J = L2J(L);
  
    /* The old state. */
    for (i = 1; i <= 4; i++) {
      setintV(lj_tab_setint(L, cur, i*2-1), J->prng.u[i-1] & 0xffffffff);
      setintV(lj_tab_setint(L, cur, i*2), J->prng.u[i-1] >> 32);
    }
  
    /* We need to set new state using the input array. */
    if (L->base < L->top && !tvisnil(L->base)) {
      PRNGState prng;
      if (tvisnumber(L->base)) {
        TValue *o = L->base;
  
        if (!tvisint(o) && ((double)(uint32_t)numV(o) != numV(o)))
          lj_err_arg(L, 1, LJ_ERR_PRNGSTATE);
  
        prng.u[0] = numberVint(o);
        for (i = 1; i < 4; i++)
          prng.u[i] = 0;
      } else {
        GCtab *t = lj_lib_checktab(L, 1);
        int i = 1, len = lj_tab_len(t);
  
        /* The input array must have at most 8 elements. */
        if (len > 8)
          lj_err_arg(L, 1, LJ_ERR_PRNGSTATE);
  
        for (i = 1; i <= len; i++) {
          cTValue *v = lj_tab_getint(t, i);
  
          if (!tvisint(v) && (!tvisnum(v) || (double)(uint32_t)numV(v) != numV(v)))
            lj_err_arg(L, 1, LJ_ERR_PRNGSTATE);
  
          if (i & 1)
            prng.u[(i-1)/2] = numberVint(v);
          else
            prng.u[(i-1)/2] = prng.u[(i-1)/2] | ((uint64_t)numberVint(v) << 32);
        }
        for (i /= 2; i < 4; i++)
          prng.u[i] = 0;
      }
  
      /* Re-initialize the JIT prng. */
      J->prng = prng;
    }
  #else
    for (int i = 1; i <= 8; i++)
      setintV(lj_tab_setint(L, cur, i), 0);
  #endif
    settabV(L, L->top++, cur);
    return 1;
  }
  
  LJLIB_PUSH(top-5) LJLIB_SET(os)
  LJLIB_PUSH(top-4) LJLIB_SET(arch)
  LJLIB_PUSH(top-3) LJLIB_SET(version_num)
  LJLIB_PUSH(top-2) LJLIB_SET(version)
  
  #include "lj_libdef.h"
  
  /* -- jit.util.* functions ------------------------------------------------ */
  
  #define LJLIB_MODULE_jit_util
  
  /* -- Reflection API for Lua functions ------------------------------------ */
  
  /* Return prototype of first argument (Lua function or prototype object) */
  static GCproto *check_Lproto(lua_State *L, int nolua)
  {
    TValue *o = L->base;
    if (L->top > o) {
      if (tvisproto(o)) {
        return protoV(o);
      } else if (tvisfunc(o)) {
        if (isluafunc(funcV(o)))
  	return funcproto(funcV(o));
        else if (nolua)
  	return NULL;
      }
    }
    lj_err_argt(L, 1, LUA_TFUNCTION);
    return NULL;  /* unreachable */
  }
  
  static void setintfield(lua_State *L, GCtab *t, const char *name, int32_t val)
  {
    setintV(lj_tab_setstr(L, t, lj_str_newz(L, name)), val);
  }
  
  /* local info = jit.util.funcinfo(func [,pc]) */
  LJLIB_CF(jit_util_funcinfo)
  {
    GCproto *pt = check_Lproto(L, 1);
    if (pt) {
      BCPos pc = (BCPos)lj_lib_optint(L, 2, 0);
      GCtab *t;
      lua_createtable(L, 0, 16);  /* Increment hash size if fields are added. */
      t = tabV(L->top-1);
      setintfield(L, t, "linedefined", pt->firstline);
      setintfield(L, t, "lastlinedefined", pt->firstline + pt->numline);
      setintfield(L, t, "stackslots", pt->framesize);
      setintfield(L, t, "params", pt->numparams);
      setintfield(L, t, "bytecodes", (int32_t)pt->sizebc);
      setintfield(L, t, "gcconsts", (int32_t)pt->sizekgc);
      setintfield(L, t, "nconsts", (int32_t)pt->sizekn);
      setintfield(L, t, "upvalues", (int32_t)pt->sizeuv);
      if (pc < pt->sizebc)
        setintfield(L, t, "currentline", lj_debug_line(pt, pc));
      lua_pushboolean(L, (pt->flags & PROTO_VARARG));
      lua_setfield(L, -2, "isvararg");
      lua_pushboolean(L, (pt->flags & PROTO_CHILD));
      lua_setfield(L, -2, "children");
      setstrV(L, L->top++, proto_chunkname(pt));
      lua_setfield(L, -2, "source");
      lj_debug_pushloc(L, pt, pc);
      lua_setfield(L, -2, "loc");
      setprotoV(L, lj_tab_setstr(L, t, lj_str_newlit(L, "proto")), pt);
    } else {
      GCfunc *fn = funcV(L->base);
      GCtab *t;
      lua_createtable(L, 0, 4);  /* Increment hash size if fields are added. */
      t = tabV(L->top-1);
      if (!iscfunc(fn))
        setintfield(L, t, "ffid", fn->c.ffid);
      setintptrV(lj_tab_setstr(L, t, lj_str_newlit(L, "addr")),
  	       (intptr_t)(void *)fn->c.f);
      setintfield(L, t, "upvalues", fn->c.nupvalues);
    }
    return 1;
  }
  
  /* local ins, m = jit.util.funcbc(func, pc) */
  LJLIB_CF(jit_util_funcbc)
  {
    GCproto *pt = check_Lproto(L, 0);
    BCPos pc = (BCPos)lj_lib_checkint(L, 2);
    int lineinfo = lj_lib_optint(L, 3, 0);
    if (pc < pt->sizebc) {
      BCIns ins = proto_bc(pt)[pc];
      BCOp op = bc_op(ins);
      lj_assertL(op < BC__MAX, "bad bytecode op %d", op);
      setintV(L->top, ins);
      setintV(L->top+1, lj_bc_mode[op]);
      L->top += 2;
      if (lineinfo) {
        setintV(L->top, lj_debug_line(pt, pc));
        L->top += 1;
        return 3;
      }
      return 2;
    }
    return 0;
  }
  
  /* local k = jit.util.funck(func, idx) */
  LJLIB_CF(jit_util_funck)
  {
    GCproto *pt = check_Lproto(L, 0);
    ptrdiff_t idx = (ptrdiff_t)lj_lib_checkint(L, 2);
    if (idx >= 0) {
      if (idx < (ptrdiff_t)pt->sizekn) {
        copyTV(L, L->top-1, proto_knumtv(pt, idx));
        return 1;
      }
    } else {
      if (~idx < (ptrdiff_t)pt->sizekgc) {
        GCobj *gc = proto_kgc(pt, idx);
        setgcV(L, L->top-1, gc, ~gc->gch.gct);
        return 1;
      }
    }
    return 0;
  }
  
  /* local name = jit.util.funcuvname(func, idx) */
  LJLIB_CF(jit_util_funcuvname)
  {
    GCproto *pt = check_Lproto(L, 0);
    uint32_t idx = (uint32_t)lj_lib_checkint(L, 2);
    if (idx < pt->sizeuv) {
      setstrV(L, L->top-1, lj_str_newz(L, lj_debug_uvname(pt, idx)));
      return 1;
    }
    return 0;
  }
  
  /* -- Reflection API for traces ------------------------------------------- */
  
  #if LJ_HASJIT
  
  /* Check trace argument. Must not throw for non-existent trace numbers. */
  static GCtrace *jit_checktrace(lua_State *L)
  {
    TraceNo tr = (TraceNo)lj_lib_checkint(L, 1);
    jit_State *J = L2J(L);
    if (tr > 0 && tr < J->sizetrace)
      return traceref(J, tr);
    return NULL;
  }
  
  /* Names of link types. ORDER LJ_TRLINK */
  static const char *const jit_trlinkname[] = {
    "none", "root", "loop", "tail-recursion", "up-recursion", "down-recursion",
    "interpreter", "return", "stitch"
  };
  
  /* local info = jit.util.traceinfo(tr) */
  LJLIB_CF(jit_util_traceinfo)
  {
    GCtrace *T = jit_checktrace(L);
    if (T) {
      GCtab *t;
      lua_createtable(L, 0, 8);  /* Increment hash size if fields are added. */
      t = tabV(L->top-1);
      setintfield(L, t, "nins", (int32_t)T->nins - REF_BIAS - 1);
      setintfield(L, t, "nk", REF_BIAS - (int32_t)T->nk);
      setintfield(L, t, "link", T->link);
      setintfield(L, t, "nexit", T->nsnap);
      setstrV(L, L->top++, lj_str_newz(L, jit_trlinkname[T->linktype]));
      lua_setfield(L, -2, "linktype");
      /* There are many more fields. Add them only when needed. */
      return 1;
    }
    return 0;
  }
  
  /* local m, ot, op1, op2, prev = jit.util.traceir(tr, idx) */
  LJLIB_CF(jit_util_traceir)
  {
    GCtrace *T = jit_checktrace(L);
    IRRef ref = (IRRef)lj_lib_checkint(L, 2) + REF_BIAS;
    if (T && ref >= REF_BIAS && ref < T->nins) {
      IRIns *ir = &T->ir[ref];
      int32_t m = lj_ir_mode[ir->o];
      setintV(L->top-2, m);
      setintV(L->top-1, ir->ot);
      setintV(L->top++, (int32_t)ir->op1 - (irm_op1(m)==IRMref ? REF_BIAS : 0));
      setintV(L->top++, (int32_t)ir->op2 - (irm_op2(m)==IRMref ? REF_BIAS : 0));
      setintV(L->top++, ir->prev);
      return 5;
    }
    return 0;
  }
  
  /* local k, t [, slot] = jit.util.tracek(tr, idx) */
  LJLIB_CF(jit_util_tracek)
  {
    GCtrace *T = jit_checktrace(L);
    IRRef ref = (IRRef)lj_lib_checkint(L, 2) + REF_BIAS;
    if (T && ref >= T->nk && ref < REF_BIAS) {
      IRIns *ir = &T->ir[ref];
      int32_t slot = -1;
      if (ir->o == IR_KSLOT) {
        slot = ir->op2;
        ir = &T->ir[ir->op1];
      }
  #if LJ_HASFFI
      if (ir->o == IR_KINT64 && !ctype_ctsG(G(L))) {
        ptrdiff_t oldtop = savestack(L, L->top);
        luaopen_ffi(L);  /* Load FFI library on-demand. */
        L->top = restorestack(L, oldtop);
      }
  #endif
      lj_ir_kvalue(L, L->top-2, ir);
      setintV(L->top-1, (int32_t)irt_type(ir->t));
      if (slot == -1)
        return 2;
      setintV(L->top++, slot);
      return 3;
    }
    return 0;
  }
  
  /* local snap = jit.util.tracesnap(tr, sn) */
  LJLIB_CF(jit_util_tracesnap)
  {
    GCtrace *T = jit_checktrace(L);
    SnapNo sn = (SnapNo)lj_lib_checkint(L, 2);
    if (T && sn < T->nsnap) {
      SnapShot *snap = &T->snap[sn];
      SnapEntry *map = &T->snapmap[snap->mapofs];
      MSize n, nent = snap->nent;
      GCtab *t;
      lua_createtable(L, nent+2, 0);
      t = tabV(L->top-1);
      setintV(lj_tab_setint(L, t, 0), (int32_t)snap->ref - REF_BIAS);
      setintV(lj_tab_setint(L, t, 1), (int32_t)snap->nslots);
      for (n = 0; n < nent; n++)
        setintV(lj_tab_setint(L, t, (int32_t)(n+2)), (int32_t)map[n]);
      setintV(lj_tab_setint(L, t, (int32_t)(nent+2)), (int32_t)SNAP(255, 0, 0));
      return 1;
    }
    return 0;
  }
  
  /* local mcode, addr, loop = jit.util.tracemc(tr) */
  LJLIB_CF(jit_util_tracemc)
  {
    GCtrace *T = jit_checktrace(L);
    if (T && T->mcode != NULL) {
      setstrV(L, L->top-1, lj_str_new(L, (const char *)T->mcode, T->szmcode));
      setintptrV(L->top++, (intptr_t)(void *)T->mcode);
      setintV(L->top++, T->mcloop);
      return 3;
    }
    return 0;
  }
  
  /* local addr = jit.util.traceexitstub([tr,] exitno) */
  LJLIB_CF(jit_util_traceexitstub)
  {
  #ifdef EXITSTUBS_PER_GROUP
    ExitNo exitno = (ExitNo)lj_lib_checkint(L, 1);
    jit_State *J = L2J(L);
    if (exitno < EXITSTUBS_PER_GROUP*LJ_MAX_EXITSTUBGR) {
      setintptrV(L->top-1, (intptr_t)(void *)exitstub_addr(J, exitno));
      return 1;
    }
  #else
    if (L->top > L->base+1) {  /* Don't throw for one-argument variant. */
      GCtrace *T = jit_checktrace(L);
      ExitNo exitno = (ExitNo)lj_lib_checkint(L, 2);
      ExitNo maxexit = T->root ? T->nsnap+1 : T->nsnap;
      if (T && T->mcode != NULL && exitno < maxexit) {
        setintptrV(L->top-1, (intptr_t)(void *)exitstub_trace_addr(T, exitno));
        return 1;
      }
    }
  #endif
    return 0;
  }
  
  /* local addr = jit.util.ircalladdr(idx) */
  LJLIB_CF(jit_util_ircalladdr)
  {
    uint32_t idx = (uint32_t)lj_lib_checkint(L, 1);
    if (idx < IRCALL__MAX) {
      setintptrV(L->top-1, (intptr_t)(void *)lj_ir_callinfo[idx].func);
      return 1;
    }
    return 0;
  }
  
  #endif
  
  #include "lj_libdef.h"
  
  static int luaopen_jit_util(lua_State *L)
  {
    LJ_LIB_REG(L, NULL, jit_util);
    return 1;
  }
  
  /* -- jit.opt module ------------------------------------------------------ */
  
  #if LJ_HASJIT
  
  #define LJLIB_MODULE_jit_opt
  
  /* Parse optimization level. */
  static int jitopt_level(jit_State *J, const char *str)
  {
    if (str[0] >= '0' && str[0] <= '9' && str[1] == '\0') {
      uint32_t flags;
      if (str[0] == '0') flags = JIT_F_OPT_0;
      else if (str[0] == '1') flags = JIT_F_OPT_1;
      else if (str[0] == '2') flags = JIT_F_OPT_2;
      else flags = JIT_F_OPT_3;
      J->flags = (J->flags & ~JIT_F_OPT_MASK) | flags;
      return 1;  /* Ok. */
    }
    return 0;  /* No match. */
  }
  
  /* Parse optimization flag. */
  static int jitopt_flag(jit_State *J, const char *str)
  {
    const char *lst = JIT_F_OPTSTRING;
    uint32_t opt;
    int set = 1;
    if (str[0] == '+') {
      str++;
    } else if (str[0] == '-') {
      str++;
      set = 0;
    } else if (str[0] == 'n' && str[1] == 'o') {
      str += str[2] == '-' ? 3 : 2;
      set = 0;
    }
    for (opt = JIT_F_OPT; ; opt <<= 1) {
      size_t len = *(const uint8_t *)lst;
      if (len == 0)
        break;
      if (strncmp(str, lst+1, len) == 0 && str[len] == '\0') {
        if (set) J->flags |= opt; else J->flags &= ~opt;
        return 1;  /* Ok. */
      }
      lst += 1+len;
    }
    return 0;  /* No match. */
  }
  
  /* Parse optimization parameter. */
  static int jitopt_param(jit_State *J, const char *str)
  {
    const char *lst = JIT_P_STRING;
    int i;
    for (i = 0; i < JIT_P__MAX; i++) {
      size_t len = *(const uint8_t *)lst;
      lj_assertJ(len != 0, "bad JIT_P_STRING");
      if (strncmp(str, lst+1, len) == 0 && str[len] == '=') {
        int32_t n = 0;
        const char *p = &str[len+1];
        while (*p >= '0' && *p <= '9')
  	n = n*10 + (*p++ - '0');
        if (*p) return 0;  /* Malformed number. */
        J->param[i] = n;
        if (i == JIT_P_hotloop)
  	lj_dispatch_init_hotcount(J2G(J));
        return 1;  /* Ok. */
      }
      lst += 1+len;
    }
    return 0;  /* No match. */
  }
  
  /* jit.opt.start(flags...) */
  LJLIB_CF(jit_opt_start)
  {
    jit_State *J = L2J(L);
    int nargs = (int)(L->top - L->base);
    if (nargs == 0) {
      J->flags = (J->flags & ~JIT_F_OPT_MASK) | JIT_F_OPT_DEFAULT;
    } else {
      int i;
      for (i = 1; i <= nargs; i++) {
        const char *str = strdata(lj_lib_checkstr(L, i));
        if (!jitopt_level(J, str) &&
  	  !jitopt_flag(J, str) &&
  	  !jitopt_param(J, str))
  	lj_err_callerv(L, LJ_ERR_JITOPT, str);
      }
    }
    return 0;
  }
  
  #include "lj_libdef.h"
  
  #endif
  
  /* -- jit.profile module -------------------------------------------------- */
  
  #if LJ_HASPROFILE
  
  #define LJLIB_MODULE_jit_profile
  
  /* Not loaded by default, use: local profile = require("jit.profile") */
  
  #define KEY_PROFILE_THREAD	(U64x(80000000,00000000)|'t')
  #define KEY_PROFILE_FUNC	(U64x(80000000,00000000)|'f')
  
  static void jit_profile_callback(lua_State *L2, lua_State *L, int samples,
  				 int vmstate)
  {
    TValue key;
    cTValue *tv;
    key.u64 = KEY_PROFILE_FUNC;
    tv = lj_tab_get(L, tabV(registry(L)), &key);
    if (tvisfunc(tv)) {
      char vmst = (char)vmstate;
      int status;
      setfuncV(L2, L2->top++, funcV(tv));
      setthreadV(L2, L2->top++, L);
      setintV(L2->top++, samples);
      setstrV(L2, L2->top++, lj_str_new(L2, &vmst, 1));
      status = lua_pcall(L2, 3, 0, 0);  /* callback(thread, samples, vmstate) */
      if (status) {
        if (G(L2)->panic) G(L2)->panic(L2);
        exit(EXIT_FAILURE);
      }
      lj_trace_abort(G(L2));
    }
  }
  
  /* profile.start(mode, cb) */
  LJLIB_CF(jit_profile_start)
  {
    GCtab *registry = tabV(registry(L));
    GCstr *mode = lj_lib_optstr(L, 1);
    GCfunc *func = lj_lib_checkfunc(L, 2);
    lua_State *L2 = lua_newthread(L);  /* Thread that runs profiler callback. */
    TValue key;
    /* Anchor thread and function in registry. */
    key.u64 = KEY_PROFILE_THREAD;
    setthreadV(L, lj_tab_set(L, registry, &key), L2);
    key.u64 = KEY_PROFILE_FUNC;
    setfuncV(L, lj_tab_set(L, registry, &key), func);
    lj_gc_anybarriert(L, registry);
    luaJIT_profile_start(L, mode ? strdata(mode) : "",
  		       (luaJIT_profile_callback)jit_profile_callback, L2);
    return 0;
  }
  
  /* profile.stop() */
  LJLIB_CF(jit_profile_stop)
  {
    GCtab *registry;
    TValue key;
    luaJIT_profile_stop(L);
    registry = tabV(registry(L));
    key.u64 = KEY_PROFILE_THREAD;
    setnilV(lj_tab_set(L, registry, &key));
    key.u64 = KEY_PROFILE_FUNC;
    setnilV(lj_tab_set(L, registry, &key));
    lj_gc_anybarriert(L, registry);
    return 0;
  }
  
  /* dump = profile.dumpstack([thread,] fmt, depth) */
  LJLIB_CF(jit_profile_dumpstack)
  {
    lua_State *L2 = L;
    int arg = 0;
    size_t len;
    int depth;
    GCstr *fmt;
    const char *p;
    if (L->top > L->base && tvisthread(L->base)) {
      L2 = threadV(L->base);
      arg = 1;
    }
    fmt = lj_lib_checkstr(L, arg+1);
    depth = lj_lib_checkint(L, arg+2);
    p = luaJIT_profile_dumpstack(L2, strdata(fmt), depth, &len);
    lua_pushlstring(L, p, len);
    return 1;
  }
  
  #include "lj_libdef.h"
  
  static int luaopen_jit_profile(lua_State *L)
  {
    LJ_LIB_REG(L, NULL, jit_profile);
    return 1;
  }
  
  #endif
  
  /* -- JIT compiler initialization ----------------------------------------- */
  
  #if LJ_HASJIT
  /* Default values for JIT parameters. */
  static const int32_t jit_param_default[JIT_P__MAX+1] = {
  #define JIT_PARAMINIT(len, name, value)	(value),
  JIT_PARAMDEF(JIT_PARAMINIT)
  #undef JIT_PARAMINIT
    0
  };
  
  #if LJ_TARGET_ARM && LJ_TARGET_LINUX
  #include <sys/utsname.h>
  #endif
  
  /* Arch-dependent CPU feature detection. */
  static uint32_t jit_cpudetect(void)
  {
    uint32_t flags = 0;
  #if LJ_TARGET_X86ORX64
  
    uint32_t vendor[4];
    uint32_t features[4];
    if (lj_vm_cpuid(0, vendor) && lj_vm_cpuid(1, features)) {
      flags |= ((features[2] >> 0)&1) * JIT_F_SSE3;
      flags |= ((features[2] >> 19)&1) * JIT_F_SSE4_1;
      if (vendor[0] >= 7) {
        uint32_t xfeatures[4];
        lj_vm_cpuid(7, xfeatures);
        flags |= ((xfeatures[1] >> 8)&1) * JIT_F_BMI2;
      }
    }
    /* Don't bother checking for SSE2 -- the VM will crash before getting here. */
  
  #elif LJ_TARGET_ARM
  
    int ver = LJ_ARCH_VERSION;  /* Compile-time ARM CPU detection. */
  #if LJ_TARGET_LINUX
    if (ver < 70) {  /* Runtime ARM CPU detection. */
      struct utsname ut;
      uname(&ut);
      if (strncmp(ut.machine, "armv", 4) == 0) {
        if (ut.machine[4] >= '8') ver = 80;
        else if (ut.machine[4] == '7') ver = 70;
        else if (ut.machine[4] == '6') ver = 60;
      }
    }
  #endif
    flags |= ver >= 70 ? JIT_F_ARMV7 :
  	   ver >= 61 ? JIT_F_ARMV6T2_ :
  	   ver >= 60 ? JIT_F_ARMV6_ : 0;
    flags |= LJ_ARCH_HASFPU == 0 ? 0 : ver >= 70 ? JIT_F_VFPV3 : JIT_F_VFPV2;
  
  #elif LJ_TARGET_ARM64
  
    /* No optional CPU features to detect (for now). */
  
  #elif LJ_TARGET_PPC
  
  #if LJ_ARCH_SQRT
    flags |= JIT_F_SQRT;
  #endif
  #if LJ_ARCH_ROUND
    flags |= JIT_F_ROUND;
  #endif
  
  #elif LJ_TARGET_MIPS
  
    /* Compile-time MIPS CPU detection. */
  #if LJ_ARCH_VERSION >= 20
    flags |= JIT_F_MIPSXXR2;
  #endif
    /* Runtime MIPS CPU detection. */
  #if defined(__GNUC__)
    if (!(flags & JIT_F_MIPSXXR2)) {
      int x;
  #ifdef __mips16
      x = 0;  /* Runtime detection is difficult. Ensure optimal -march flags. */
  #else
      /* On MIPS32R1 rotr is treated as srl. rotr r2,r2,1 -> srl r2,r2,1. */
      __asm__("li $2, 1\n\t.long 0x00221042\n\tmove %0, $2" : "=r"(x) : : "$2");
  #endif
      if (x) flags |= JIT_F_MIPSXXR2;  /* Either 0x80000000 (R2) or 0 (R1). */
    }
  #endif
  #elif LJ_TARGET_S390X
    /* No optional CPU features to detect (for now). */
  #else
  #error "Missing CPU detection for this architecture"
  #endif
    return flags;
  }
  
  /* Initialize JIT compiler. */
  static void jit_init(lua_State *L)
  {
    jit_State *J = L2J(L);
    J->flags = jit_cpudetect() | JIT_F_ON | JIT_F_OPT_DEFAULT;
    memcpy(J->param, jit_param_default, sizeof(J->param));
    lj_dispatch_update(G(L));
  }
  #endif
  
  LUALIB_API int luaopen_jit(lua_State *L)
  {
  #if LJ_HASJIT
    jit_init(L);
  #endif
    lua_pushliteral(L, LJ_OS_NAME);
    lua_pushliteral(L, LJ_ARCH_NAME);
    lua_pushinteger(L, LUAJIT_VERSION_NUM);
    lua_pushliteral(L, LUAJIT_VERSION);
    LJ_LIB_REG(L, LUA_JITLIBNAME, jit);
  #if LJ_HASPROFILE
    lj_lib_prereg(L, LUA_JITLIBNAME ".profile", luaopen_jit_profile,
  		tabref(L->env));
  #endif
  #ifndef LUAJIT_DISABLE_JITUTIL
    lj_lib_prereg(L, LUA_JITLIBNAME ".util", luaopen_jit_util, tabref(L->env));
  #endif
  #if LJ_HASJIT
    LJ_LIB_REG(L, "jit.opt", jit_opt);
  #endif
    L->top -= 2;
    return 1;
  }
  
  