Browse Source

FFI: Box all accessed or returned enums.

Mike Pall 13 years ago
parent
commit
4d9c29a78c
11 changed files with 121 additions and 60 deletions
  1. 16 7
      doc/ext_ffi_semantics.html
  2. 3 1
      src/lib_ffi.c
  3. 1 1
      src/lj_asm_arm.h
  4. 3 2
      src/lj_asm_x86.h
  5. 28 3
      src/lj_carith.c
  6. 0 1
      src/lj_ccall.c
  7. 0 1
      src/lj_ccallback.c
  8. 0 1
      src/lj_cconv.c
  9. 2 2
      src/lj_cdata.c
  10. 66 41
      src/lj_crecord.c
  11. 2 0
      src/lj_opt_fold.c

+ 16 - 7
doc/ext_ffi_semantics.html

@@ -250,19 +250,20 @@ constant values; retrieving return values from C calls:
 <tr class="even separate">
 <td class="convin"><tt>bool</tt></td><td class="convop">0 &rarr; <tt>false</tt>, otherwise <tt>true</tt></td><td class="convout">boolean</td></tr>
 <tr class="odd separate">
-<td class="convin">Complex number</td><td class="convop">boxed value</td><td class="convout">complex cdata</td></tr>
+<td class="convin"><tt>enum</tt></td><td class="convop">boxed value</td><td class="convout">enum cdata</td></tr>
 <tr class="even">
-<td class="convin">Vector</td><td class="convop">boxed value</td><td class="convout">vector cdata</td></tr>
+<td class="convin">Complex number</td><td class="convop">boxed value</td><td class="convout">complex cdata</td></tr>
 <tr class="odd">
+<td class="convin">Vector</td><td class="convop">boxed value</td><td class="convout">vector cdata</td></tr>
+<tr class="even">
 <td class="convin">Pointer</td><td class="convop">boxed value</td><td class="convout">pointer cdata</td></tr>
-<tr class="even separate">
+<tr class="odd separate">
 <td class="convin">Array</td><td class="convop">boxed reference</td><td class="convout">reference cdata</td></tr>
-<tr class="odd">
+<tr class="even">
 <td class="convin"><tt>struct</tt>/<tt>union</tt></td><td class="convop">boxed reference</td><td class="convout">reference cdata</td></tr>
 </table>
 <p>
-Bitfields or <tt>enum</tt> types are treated like their underlying
-type.
+Bitfields are treated like their underlying type.
 </p>
 <p>
 Reference types are dereferenced <em>before</em> a conversion can take
@@ -715,6 +716,10 @@ is performed. Otherwise both sides are converted to an
 <tt>int64_t</tt> and a signed arithmetic operation is performed. The
 result is a boxed 64&nbsp;bit cdata object.<br>
 
+If one of the operands is an <tt>enum</tt> and the other operand is a
+string, the string is converted to the value of a matching <tt>enum</tt>
+constant before the above conversion.<br>
+
 These rules ensure that 64&nbsp;bit integers are "sticky". Any
 expression involving at least one 64&nbsp;bit integer operand results
 in another one. The undefined cases for the division, modulo and power
@@ -740,7 +745,11 @@ cdata number and a Lua number can be compared with each other. If one
 of them is an <tt>uint64_t</tt>, the other side is converted to an
 <tt>uint64_t</tt> and an unsigned comparison is performed. Otherwise
 both sides are converted to an <tt>int64_t</tt> and a signed
-comparison is performed.</li>
+comparison is performed.<br>
+
+If one of the operands is an <tt>enum</tt> and the other operand is a
+string, the string is converted to the value of a matching <tt>enum</tt>
+constant before the above conversion.<br>
 
 <li><b>Comparisons for equality/inequality</b> never raise an error.
 Even incompatible pointers can be compared for equality by address. Any

+ 3 - 1
src/lib_ffi.c

@@ -297,6 +297,9 @@ LJLIB_CF(ffi_meta___tostring)
       goto checkgc;
     } else if (ctype_isfunc(ct->info)) {
       p = *(void **)p;
+    } else if (ctype_isenum(ct->info)) {
+      msg = "cdata<%s>: %d";
+      p = (void *)(uintptr_t)*(uint32_t **)p;
     } else {
       if (ctype_isptr(ct->info)) {
 	p = cdata_getptr(p, ct->size);
@@ -348,7 +351,6 @@ LJLIB_CF(ffi_clib___index)	LJLIB_REC(clib_index 1)
       CTypeID sid = ctype_cid(s->info);
       void *sp = *(void **)cdataptr(cd);
       CType *ct = ctype_raw(cts, sid);
-      if (ctype_isenum(ct->info)) ct = ctype_child(cts, ct);
       if (lj_cconv_tv_ct(cts, ct, sid, L->top-1, sp))
 	lj_gc_check(L);
       return 1;

+ 1 - 1
src/lj_asm_arm.h

@@ -1287,7 +1287,7 @@ static void asm_intcomp(ASMState *as, IRIns *ir)
   Reg left;
   uint32_t m;
   int cmpprev0 = 0;
-  lua_assert(irt_isint(ir->t) || irt_isaddr(ir->t));
+  lua_assert(irt_isint(ir->t) || irt_isu32(ir->t) || irt_isaddr(ir->t));
   if (asm_swapops(as, lref, rref)) {
     Reg tmp = lref; lref = rref; rref = tmp;
     if (cc >= CC_GE) cc ^= 7;  /* LT <-> GT, LE <-> GE */

+ 3 - 2
src/lj_asm_x86.h

@@ -337,7 +337,7 @@ static Reg asm_fuseload(ASMState *as, IRRef ref, RegSet allow)
       }
     } else if (ir->o == IR_FLOAD) {
       /* Generic fusion is only ok for 32 bit operand (but see asm_comp). */
-      if ((irt_isint(ir->t) || irt_isaddr(ir->t)) &&
+      if ((irt_isint(ir->t) || irt_isu32(ir->t) || irt_isaddr(ir->t)) &&
 	  noconflict(as, ref, IR_FSTORE, 0)) {
 	asm_fusefref(as, ir, xallow);
 	return RID_MRM;
@@ -2064,7 +2064,8 @@ static void asm_comp(ASMState *as, IRIns *ir, uint32_t cc)
     IROp leftop = (IROp)(IR(lref)->o);
     Reg r64 = REX_64IR(ir, 0);
     int32_t imm = 0;
-    lua_assert(irt_is64(ir->t) || irt_isint(ir->t) || irt_isaddr(ir->t));
+    lua_assert(irt_is64(ir->t) || irt_isint(ir->t) ||
+	       irt_isu32(ir->t) || irt_isaddr(ir->t));
     /* Swap constants (only for ABC) and fusable loads to the right. */
     if (irref_isk(lref) || (!irref_isk(rref) && opisfusableload(leftop))) {
       if ((cc & 0xc) == 0xc) cc ^= 0x53;  /* L <-> G, LE <-> GE */

+ 28 - 3
src/lj_carith.c

@@ -46,6 +46,7 @@ static int carith_checkarg(lua_State *L, CTState *cts, CDArith *ca)
 	ct = ctype_get(cts,
 	  lj_ctype_intern(cts, CTINFO(CT_PTR, CTALIGN_PTR|id), CTSIZE_PTR));
       }
+      if (ctype_isenum(ct->info)) ct = ctype_child(cts, ct);
       ca->ct[i] = ct;
       ca->p[i] = p;
     } else if (tvisint(o)) {
@@ -57,6 +58,25 @@ static int carith_checkarg(lua_State *L, CTState *cts, CDArith *ca)
     } else if (tvisnil(o)) {
       ca->ct[i] = ctype_get(cts, CTID_P_VOID);
       ca->p[i] = (uint8_t *)0;
+    } else if (tvisstr(o)) {
+      TValue *o2 = i == 0 ? o+1 : o-1;
+      CType *ct = ctype_raw(cts, cdataV(o2)->ctypeid);
+      ca->ct[i] = NULL;
+      ca->p[i] = NULL;
+      ok = 0;
+      if (ctype_isenum(ct->info)) {
+	CTSize ofs;
+	CType *cct = lj_ctype_getfield(cts, ct, strV(o), &ofs);
+	if (cct && ctype_isconstval(cct->info)) {
+	  ca->ct[i] = ctype_child(cts, cct);
+	  ca->p[i] = (uint8_t *)&cct->size;  /* Assumes ct does not grow. */
+	  ok = 1;
+	} else {
+	  ca->ct[1-i] = ct;  /* Use enum to improve error message. */
+	  ca->p[1-i] = NULL;
+	  break;
+	}
+      }
     } else {
       ca->ct[i] = NULL;
       ca->p[i] = NULL;
@@ -204,17 +224,22 @@ static int lj_carith_meta(lua_State *L, CTState *cts, CDArith *ca, MMS mm)
     tv = lj_ctype_meta(cts, cdataV(L->base+1)->ctypeid, mm);
   if (!tv) {
     const char *repr[2];
-    int i;
+    int i, isenum = -1, isstr = -1;
     if (mm == MM_eq) {  /* Equality checks never raise an error. */
       setboolV(L->top-1, 0);
       return 1;
     }
     for (i = 0; i < 2; i++) {
-      if (ca->ct[i])
+      if (ca->ct[i]) {
+	if (ctype_isenum(ca->ct[i]->info)) isenum = i;
 	repr[i] = strdata(lj_ctype_repr(L, ctype_typeid(cts, ca->ct[i]), NULL));
-      else
+      } else {
+	if (tvisstr(&L->base[i])) isstr = i;
 	repr[i] = lj_typename(&L->base[i]);
+      }
     }
+    if ((isenum ^ isstr) == 1)
+      lj_err_callerv(L, LJ_ERR_FFI_BADCONV, repr[isstr], repr[isenum]);
     lj_err_callerv(L, mm == MM_len ? LJ_ERR_FFI_BADLEN :
 		      mm == MM_concat ? LJ_ERR_FFI_BADCONCAT :
 		      mm < MM_add ? LJ_ERR_FFI_BADCOMP : LJ_ERR_FFI_BADARITH,

+ 0 - 1
src/lj_ccall.c

@@ -684,7 +684,6 @@ static int ccall_get_results(lua_State *L, CTState *cts, CType *ct,
 #endif
   /* No reference types end up here, so there's no need for the CTypeID. */
   lua_assert(!(ctype_isrefarray(ctr->info) || ctype_isstruct(ctr->info)));
-  if (ctype_isenum(ctr->info)) ctr = ctype_child(cts, ctr);
   return lj_cconv_tv_ct(cts, ctr, 0, L->top-1, sp);
 }
 

+ 0 - 1
src/lj_ccallback.c

@@ -426,7 +426,6 @@ static void callback_conv_args(CTState *cts, lua_State *L)
       MSize n;
       lua_assert(ctype_isfield(ctf->info));
       cta = ctype_rawchild(cts, ctf);
-      if (ctype_isenum(cta->info)) cta = ctype_child(cts, cta);
       isfp = ctype_isfp(cta->info);
       sz = (cta->size + CTSIZE_PTR-1) & ~(CTSIZE_PTR-1);
       n = sz / CTSIZE_PTR;  /* Number of GPRs or stack slots needed. */

+ 0 - 1
src/lj_cconv.c

@@ -374,7 +374,6 @@ int lj_cconv_tv_ct(CTState *cts, CType *s, CTypeID sid,
 		   TValue *o, uint8_t *sp)
 {
   CTInfo sinfo = s->info;
-  lua_assert(!ctype_isenum(sinfo));
   if (ctype_isnum(sinfo)) {
     if (!ctype_isbool(sinfo)) {
       if (ctype_isinteger(sinfo) && s->size > 4) goto copyval;

+ 2 - 2
src/lj_cdata.c

@@ -231,8 +231,8 @@ int lj_cdata_get(CTState *cts, CType *s, TValue *o, uint8_t *sp)
     s = ctype_get(cts, sid);
   }
 
-  /* Skip attributes and enums. */
-  while (ctype_isattrib(s->info) || ctype_isenum(s->info))
+  /* Skip attributes. */
+  while (ctype_isattrib(s->info))
     s = ctype_child(cts, s);
 
   return lj_cconv_tv_ct(cts, s, sid, o, sp);

+ 66 - 41
src/lj_crecord.c

@@ -111,8 +111,9 @@ static CTypeID argv2ctype(jit_State *J, TRef tr, cTValue *o)
 */
 
 /* Convert CType to IRType. */
-static IRType crec_ct2irt(CType *ct)
+static IRType crec_ct2irt(CTState *cts, CType *ct)
 {
+  if (ctype_isenum(ct->info)) ct = ctype_child(cts, ct);
   if (LJ_LIKELY(ctype_isnum(ct->info))) {
     if ((ct->info & CTF_FP)) {
       if (ct->size == sizeof(double))
@@ -162,10 +163,10 @@ static int crec_isnonzero(CType *s, void *p)
 static TRef crec_ct_ct(jit_State *J, CType *d, CType *s, TRef dp, TRef sp,
 		       void *svisnz)
 {
+  IRType dt = crec_ct2irt(ctype_ctsG(J2G(J)), d);
+  IRType st = crec_ct2irt(ctype_ctsG(J2G(J)), s);
   CTSize dsize = d->size, ssize = s->size;
   CTInfo dinfo = d->info, sinfo = s->info;
-  IRType dt = crec_ct2irt(d);
-  IRType st = crec_ct2irt(s);
 
   if (ctype_type(dinfo) > CT_MAYCONVERT || ctype_type(sinfo) > CT_MAYCONVERT)
     goto err_conv;
@@ -317,10 +318,9 @@ static TRef crec_ct_ct(jit_State *J, CType *d, CType *s, TRef dp, TRef sp,
 static TRef crec_tv_ct(jit_State *J, CType *s, CTypeID sid, TRef sp)
 {
   CTState *cts = ctype_ctsG(J2G(J));
+  IRType t = crec_ct2irt(cts, s);
   CTInfo sinfo = s->info;
-  lua_assert(!ctype_isenum(sinfo));
   if (ctype_isnum(sinfo)) {
-    IRType t = crec_ct2irt(s);
     TRef tr;
     if (t == IRT_CDATA)
       goto err_nyi;  /* NYI: copyval of >64 bit integers. */
@@ -338,14 +338,12 @@ static TRef crec_tv_ct(jit_State *J, CType *s, CTypeID sid, TRef sp)
     } else {
       return tr;
     }
-  } else if (ctype_isptr(sinfo)) {
-    IRType t = (LJ_64 && s->size == 8) ? IRT_P64 : IRT_P32;
-    sp = emitir(IRT(IR_XLOAD, t), sp, 0);
+  } else if (ctype_isptr(sinfo) || ctype_isenum(sinfo)) {
+    sp = emitir(IRT(IR_XLOAD, t), sp, 0);  /* Box pointers and enums. */
   } else if (ctype_isrefarray(sinfo) || ctype_isstruct(sinfo)) {
     cts->L = J->L;
     sid = lj_ctype_intern(cts, CTINFO_REF(sid), CTSIZE_PTR);  /* Create ref. */
   } else if (ctype_iscomplex(sinfo)) {  /* Unbox/box complex. */
-    IRType t = s->size == 2*sizeof(double) ? IRT_NUM : IRT_FLOAT;
     ptrdiff_t esz = (ptrdiff_t)(s->size >> 1);
     TRef ptr, tr1, tr2, dp;
     dp = emitir(IRTG(IR_CNEW, IRT_CDATA), lj_ir_kint(J, sid), TREF_NIL);
@@ -362,7 +360,7 @@ static TRef crec_tv_ct(jit_State *J, CType *s, CTypeID sid, TRef sp)
   err_nyi:
     lj_trace_err(J, LJ_TRERR_NYICONV);
   }
-  /* Box pointer, ref or 64 bit integer. */
+  /* Box pointer, ref, enum or 64 bit integer. */
   return emitir(IRTG(IR_CNEWI, IRT_CDATA), lj_ir_kint(J, sid), sp);
 }
 
@@ -403,8 +401,8 @@ static TRef crec_ct_tv(jit_State *J, CType *d, TRef dp, TRef sp, cTValue *sval)
       emitir(IRTG(IR_EQ, IRT_STR), sp, lj_ir_kstr(J, str));
       if (cct && ctype_isconstval(cct->info)) {
 	lua_assert(ctype_child(cts, cct)->size == 4);
-	svisnz = (void *)(intptr_t)(cct->size != 0);
-	sp = lj_ir_kint(J, (int32_t)cct->size);
+	svisnz = (void *)(intptr_t)(ofs != 0);
+	sp = lj_ir_kint(J, (int32_t)ofs);
 	sid = ctype_cid(cct->info);
       }  /* else: interpreter will throw. */
     } else if (ctype_isrefarray(d->info)) {  /* Copy string to array. */
@@ -418,15 +416,13 @@ static TRef crec_ct_tv(jit_State *J, CType *d, TRef dp, TRef sp, cTValue *sval)
     sid = argv2cdata(J, sp, sval)->ctypeid;
     s = ctype_raw(cts, sid);
     svisnz = cdataptr(cdataV(sval));
-    if (ctype_isenum(s->info)) s = ctype_child(cts, s);
-    t = crec_ct2irt(s);
+    t = crec_ct2irt(cts, s);
     if (ctype_isptr(s->info)) {
       sp = emitir(IRT(IR_FLOAD, t), sp, IRFL_CDATA_PTR);
       if (ctype_isref(s->info)) {
 	svisnz = *(void **)svisnz;
 	s = ctype_rawchild(cts, s);
-	if (ctype_isenum(s->info)) s = ctype_child(cts, s);
-	t = crec_ct2irt(s);
+	t = crec_ct2irt(cts, s);
       } else {
 	goto doconv;
       }
@@ -554,10 +550,8 @@ again:
   } else if (tref_iscdata(idx)) {
     GCcdata *cdk = cdataV(&rd->argv[1]);
     CType *ctk = ctype_raw(cts, cdk->ctypeid);
-    IRType t;
-    if (ctype_isenum(ctk->info)) ctk = ctype_child(cts, ctk);
-    if (ctype_ispointer(ct->info) &&
-	ctype_isinteger(ctk->info) && (t = crec_ct2irt(ctk)) != IRT_CDATA) {
+    IRType t = crec_ct2irt(cts, ctk);
+    if (ctype_ispointer(ct->info) && t >= IRT_I8 && t <= IRT_U64) {
       if (ctk->size == 8) {
 	idx = emitir(IRT(IR_FLOAD, t), idx, IRFL_CDATA_INT64);
       } else if (ctk->size == 4) {
@@ -637,7 +631,6 @@ again:
     ct = ctype_child(cts, ct);  /* Skip attributes. */
 
   if (rd->data == 0) {  /* __index metamethod. */
-    if (ctype_isenum(ct->info)) ct = ctype_child(cts, ct);  /* Skip enums. */
     J->base[0] = crec_tv_ct(J, ct, sid, ptr);
   } else {  /* __newindex metamethod. */
     rd->nres = 0;
@@ -888,7 +881,7 @@ static int crec_call(jit_State *J, RecordFFData *rd, GCcdata *cd)
   if (ctype_isfunc(ct->info)) {
     TRef func = emitir(IRT(IR_FLOAD, tp), J->base[0], IRFL_CDATA_PTR);
     CType *ctr = ctype_rawchild(cts, ct);
-    IRType t = crec_ct2irt(ctr);
+    IRType t = crec_ct2irt(cts, ctr);
     TRef tr;
     TValue tv;
     /* Check for blacklisted C functions that might call a callback. */
@@ -899,12 +892,10 @@ static int crec_call(jit_State *J, RecordFFData *rd, GCcdata *cd)
     if (ctype_isvoid(ctr->info)) {
       t = IRT_NIL;
       rd->nres = 0;
-    } else if (ctype_isenum(ctr->info)) {
-      ctr = ctype_child(cts, ctr);
-    }
-    if (!(ctype_isnum(ctr->info) || ctype_isptr(ctr->info) ||
-	  ctype_isvoid(ctr->info)) || t == IRT_CDATA)
+    } else if (!(ctype_isnum(ctr->info) || ctype_isptr(ctr->info) ||
+		 ctype_isenum(ctr->info)) || t == IRT_CDATA) {
       lj_trace_err(J, LJ_TRERR_NYICALL);
+    }
     if ((ct->info & CTF_VARARG)
 #if LJ_TARGET_X86
 	|| ctype_cconv(ct->info) != CTCC_CDECL
@@ -923,17 +914,17 @@ static int crec_call(jit_State *J, RecordFFData *rd, GCcdata *cd)
 	J->postproc = LJ_POST_FIXGUARDSNAP;
 	tr = TREF_TRUE;
       }
+    } else if (t == IRT_PTR || (LJ_64 && t == IRT_P32) ||
+	       t == IRT_I64 || t == IRT_U64 || ctype_isenum(ctr->info)) {
+      TRef trid = lj_ir_kint(J, ctype_cid(ct->info));
+      tr = emitir(IRTG(IR_CNEWI, IRT_CDATA), trid, tr);
+      if (t == IRT_I64 || t == IRT_U64) lj_needsplit(J);
     } else if (t == IRT_FLOAT || t == IRT_U32) {
       tr = emitconv(tr, IRT_NUM, t, 0);
     } else if (t == IRT_I8 || t == IRT_I16) {
       tr = emitconv(tr, IRT_INT, t, IRCONV_SEXT);
     } else if (t == IRT_U8 || t == IRT_U16) {
       tr = emitconv(tr, IRT_INT, t, 0);
-    } else if (t == IRT_PTR || (LJ_64 && t == IRT_P32) ||
-	       (t == IRT_I64 || t == IRT_U64)) {
-      TRef trid = lj_ir_kint(J, ctype_cid(ct->info));
-      tr = emitir(IRTG(IR_CNEWI, IRT_CDATA), trid, tr);
-      if (t == IRT_I64 || t == IRT_U64) lj_needsplit(J);
     }
     J->base[0] = tr;
     J->needsnap = 1;
@@ -981,12 +972,25 @@ static TRef crec_arith_int64(jit_State *J, TRef *sp, CType **s, MMS mm)
     CTypeID id;
     TRef tr;
     MSize i;
+    IROp op;
     lj_needsplit(J);
     if (((s[0]->info & CTF_UNSIGNED) && s[0]->size == 8) ||
 	((s[1]->info & CTF_UNSIGNED) && s[1]->size == 8)) {
       dt = IRT_U64; id = CTID_UINT64;
     } else {
       dt = IRT_I64; id = CTID_INT64;
+      if (mm < MM_add &&
+	  !((s[0]->info | s[1]->info) & CTF_FP) &&
+	  s[0]->size == 4 && s[1]->size == 4) {  /* Try to narrow comparison. */
+	if (!((s[0]->info ^ s[1]->info) & CTF_UNSIGNED) ||
+	    (tref_isk(sp[1]) && IR(tref_ref(sp[1]))->i >= 0)) {
+	  dt = (s[0]->info & CTF_UNSIGNED) ? IRT_U32 : IRT_INT;
+	  goto comp;
+	} else if (tref_isk(sp[0]) && IR(tref_ref(sp[0]))->i >= 0) {
+	  dt = (s[1]->info & CTF_UNSIGNED) ? IRT_U32 : IRT_INT;
+	  goto comp;
+	}
+      }
     }
     for (i = 0; i < 2; i++) {
       IRType st = tref_type(sp[i]);
@@ -994,16 +998,16 @@ static TRef crec_arith_int64(jit_State *J, TRef *sp, CType **s, MMS mm)
 	sp[i] = emitconv(sp[i], dt, st, IRCONV_TRUNC|IRCONV_ANY);
       else if (!(st == IRT_I64 || st == IRT_U64))
 	sp[i] = emitconv(sp[i], dt, IRT_INT,
-			 ((st - IRT_I8) & 1) ? 0 : IRCONV_SEXT);
+			 (s[i]->info & CTF_UNSIGNED) ? 0 : IRCONV_SEXT);
     }
     if (mm < MM_add) {
+    comp:
       /* Assume true comparison. Fixup and emit pending guard later. */
-      IROp op;
       if (mm == MM_eq) {
 	op = IR_EQ;
       } else {
 	op = mm == MM_lt ? IR_LT : IR_LE;
-	if (dt == IRT_U64)
+	if (dt == IRT_U32 || dt == IRT_U64)
 	  op += (IR_ULT-IR_LT);
       }
       lj_ir_set(J, IRTG(op, dt), sp[0], sp[1]);
@@ -1116,26 +1120,33 @@ void LJ_FASTCALL recff_cdata_arith(jit_State *J, RecordFFData *rd)
       goto trymeta;
     } else if (tref_iscdata(tr)) {
       CTypeID id = argv2cdata(J, tr, &rd->argv[i])->ctypeid;
+      IRType t;
       ct = ctype_raw(cts, id);
+      t = crec_ct2irt(cts, ct);
       if (ctype_isptr(ct->info)) {  /* Resolve pointer or reference. */
-	IRType t = (LJ_64 && ct->size == 8) ? IRT_P64 : IRT_P32;
-	if (ctype_isref(ct->info)) ct = ctype_rawchild(cts, ct);
 	tr = emitir(IRT(IR_FLOAD, t), tr, IRFL_CDATA_PTR);
-      } else if (ctype_isinteger(ct->info) && ct->size == 8) {
-	IRType t = (ct->info & CTF_UNSIGNED) ? IRT_U64 : IRT_I64;
+	if (ctype_isref(ct->info)) {
+	  ct = ctype_rawchild(cts, ct);
+	  t = crec_ct2irt(cts, ct);
+	}
+      } else if (t == IRT_I64 || t == IRT_U64) {
 	tr = emitir(IRT(IR_FLOAD, t), tr, IRFL_CDATA_INT64);
 	lj_needsplit(J);
 	goto ok;
+      } else if (t == IRT_INT || t == IRT_U32) {
+	tr = emitir(IRT(IR_FLOAD, t), tr, IRFL_CDATA_INT);
+	if (ctype_isenum(ct->info)) ct = ctype_child(cts, ct);
+	goto ok;
       } else if (ctype_isfunc(ct->info)) {
 	tr = emitir(IRT(IR_FLOAD, IRT_PTR), tr, IRFL_CDATA_PTR);
 	ct = ctype_get(cts,
 	  lj_ctype_intern(cts, CTINFO(CT_PTR, CTALIGN_PTR|id), CTSIZE_PTR));
+	goto ok;
       } else {
 	tr = emitir(IRT(IR_ADD, IRT_PTR), tr, lj_ir_kintp(J, sizeof(GCcdata)));
       }
       if (ctype_isenum(ct->info)) ct = ctype_child(cts, ct);
       if (ctype_isnum(ct->info)) {
-	IRType t = crec_ct2irt(ct);
 	if (t == IRT_CDATA) goto trymeta;
 	if (t == IRT_I64 || t == IRT_U64) lj_needsplit(J);
 	tr = emitir(IRT(IR_XLOAD, t), tr, 0);
@@ -1147,6 +1158,21 @@ void LJ_FASTCALL recff_cdata_arith(jit_State *J, RecordFFData *rd)
       ct = ctype_get(cts, CTID_P_VOID);
     } else if (tref_isinteger(tr)) {
       ct = ctype_get(cts, CTID_INT32);
+    } else if (tref_isstr(tr)) {
+      TRef tr2 = J->base[1-i];
+      CTypeID id = argv2cdata(J, tr2, &rd->argv[1-i])->ctypeid;
+      ct = ctype_raw(cts, id);
+      if (ctype_isenum(ct->info)) {  /* Match string against enum constant. */
+	GCstr *str = strV(&rd->argv[i]);
+	CTSize ofs;
+	CType *cct = lj_ctype_getfield(cts, ct, str, &ofs);
+	if (cct && ctype_isconstval(cct->info)) {
+	  /* Specialize to the name of the enum constant. */
+	  emitir(IRTG(IR_EQ, IRT_STR), tr, lj_ir_kstr(J, str));
+	  ct = ctype_child(cts, cct);
+	  tr = lj_ir_kint(J, (int32_t)ofs);
+	}  /* else: interpreter will throw. */
+      }  /* else: interpreter will throw. */
     } else if (!tref_isnum(tr)) {
       goto trymeta;
     }
@@ -1203,7 +1229,6 @@ void LJ_FASTCALL recff_clib_index(jit_State *J, RecordFFData *rd)
 	void *sp = *(void **)cdataptr(cdataV(tv));
 	TRef ptr;
 	ct = ctype_raw(cts, sid);
-	if (rd->data && ctype_isenum(ct->info)) ct = ctype_child(cts, ct);
 	if (LJ_64 && !checkptr32(sp))
 	  ptr = lj_ir_kintp(J, (uintptr_t)sp);
 	else

+ 2 - 0
src/lj_opt_fold.c

@@ -575,6 +575,8 @@ LJFOLDF(kfold_conv_kintu32_num)
 
 LJFOLD(CONV KINT IRCONV_I64_INT)
 LJFOLD(CONV KINT IRCONV_U64_INT)
+LJFOLD(CONV KINT IRCONV_I64_U32)
+LJFOLD(CONV KINT IRCONV_U64_U32)
 LJFOLDF(kfold_conv_kint_i64)
 {
   if ((fins->op2 & IRCONV_SEXT))