diff --git a/doc/ext_ffi_semantics.html b/doc/ext_ffi_semantics.html index 9016a8a6..b8c839c2 100644 --- a/doc/ext_ffi_semantics.html +++ b/doc/ext_ffi_semantics.html @@ -985,7 +985,6 @@ alignment > 8 bytes.
  • Conversions from lightuserdata to void *.
  • Pointer differences for element sizes that are not a power of two.
  • -
  • Calls to non-cdecl or vararg C functions.
  • Calls to C functions with aggregates passed or returned by value.
  • Calls to ctype metamethods which are not plain functions.
  • diff --git a/lib/dump.lua b/lib/dump.lua index 3a88ef45..0f0e9058 100644 --- a/lib/dump.lua +++ b/lib/dump.lua @@ -378,6 +378,24 @@ local function ridsp_name(ridsp) return "" end +-- Dump CALL* function ref and return optional ctype. +local function dumpcallfunc(tr, ins) + local ctype + if ins > 0 then + local m, ot, op1, op2 = traceir(tr, ins) + if band(ot, 31) == 0 then -- nil type means CARG(func, ctype). + ins = op1 + ctype = formatk(tr, op2) + end + end + if ins < 0 then + out:write(format("[0x%x](", tonumber((tracek(tr, ins))))) + else + out:write(format("%04d (", ins)) + end + return ctype +end + -- Recursively gather CALL* args and dump them. local function dumpcallargs(tr, ins) if ins < 0 then @@ -447,15 +465,15 @@ local function dump_ir(tr, dumpsnap, dumpreg) irtype[t], op)) local m1, m2 = band(m, 3), band(m, 3*4) if sub(op, 1, 4) == "CALL" then + local ctype if m2 == 1*4 then -- op2 == IRMlit out:write(format("%-10s (", vmdef.ircall[op2])) - elseif op2 < 0 then - out:write(format("[0x%x](", tonumber((tracek(tr, op2))))) else - out:write(format("%04d (", op2)) + ctype = dumpcallfunc(tr, op2) end if op1 ~= -1 then dumpcallargs(tr, op1) end out:write(")") + if ctype then out:write(" ctype ", ctype) end elseif op == "CNEW " and op2 == -1 then out:write(formatk(tr, op1)) elseif m1 ~= 3 then -- op1 != IRMnone diff --git a/src/Makefile.dep b/src/Makefile.dep index 9c866050..81bbed29 100644 --- a/src/Makefile.dep +++ b/src/Makefile.dep @@ -1,6 +1,6 @@ buildvm.o: buildvm.c buildvm.h lj_def.h lua.h luaconf.h lj_arch.h \ lj_obj.h lj_gc.h lj_bc.h lj_ir.h lj_ircall.h lj_jit.h lj_frame.h \ - lj_dispatch.h lj_ccall.h luajit.h \ + lj_dispatch.h lj_ccall.h lj_ctype.h luajit.h \ lj_traceerr.h buildvm_asm.o: buildvm_asm.c buildvm.h lj_def.h lua.h luaconf.h lj_arch.h \ lj_bc.h @@ -86,9 +86,9 @@ lj_cparse.o: lj_cparse.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ lj_bc.h lj_vm.h lj_char.h lj_crecord.o: lj_crecord.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_frame.h lj_bc.h lj_ctype.h \ - lj_gc.h lj_cparse.h lj_cconv.h lj_clib.h lj_ir.h lj_jit.h lj_ircall.h \ - lj_iropt.h lj_trace.h lj_dispatch.h lj_traceerr.h lj_record.h \ - lj_ffrecord.h lj_crecord.h + lj_gc.h lj_cparse.h lj_cconv.h lj_clib.h lj_ccall.h lj_ir.h lj_jit.h \ + lj_ircall.h lj_iropt.h lj_trace.h lj_dispatch.h lj_traceerr.h \ + lj_record.h lj_ffrecord.h lj_crecord.h lj_ctype.o: lj_ctype.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_ctype.h lj_debug.o: lj_debug.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ diff --git a/src/lj_asm.c b/src/lj_asm.c index 2e204239..9fe53416 100644 --- a/src/lj_asm.c +++ b/src/lj_asm.c @@ -888,7 +888,16 @@ static uint32_t asm_callx_flags(ASMState *as, IRIns *ir) nargs++; while (ira->o == IR_CARG) { nargs++; ira = IR(ira->op1); } } - /* NYI: fastcall etc. */ +#if LJ_HASFFI + if (IR(ir->op2)->o == IR_CARG) { /* Copy calling convention info. */ + CTypeID id = (CTypeID)IR(IR(ir->op2)->op2)->i; + CType *ct = ctype_get(ctype_ctsG(J2G(as->J)), id); + nargs |= ((ct->info & CTF_VARARG) ? CCI_VARARG : 0); +#if LJ_TARGET_X86 + nargs |= (ctype_cconv(ct->info) << CCI_CC_SHIFT); +#endif + } +#endif return (nargs | (ir->t.irt << CCI_OTSHIFT)); } diff --git a/src/lj_asm_arm.h b/src/lj_asm_arm.h index 1963f3ba..2d4b8bae 100644 --- a/src/lj_asm_arm.h +++ b/src/lj_asm_arm.h @@ -331,13 +331,17 @@ static void asm_callx(ASMState *as, IRIns *ir) { IRRef args[CCI_NARGS_MAX]; CCallInfo ci; + IRRef func; + IRIns *irf; ci.flags = asm_callx_flags(as, ir); asm_collectargs(as, ir, &ci, args); asm_setupresult(as, ir, &ci); - if (irref_isk(ir->op2)) { /* Call to constant address. */ - ci.func = (ASMFunction)(void *)(IR(ir->op2)->i); + func = ir->op2; irf = IR(func); + if (irf->o == IR_CARG) { func = irf->op1; irf = IR(func); } + if (irref_isk(func)) { /* Call to constant address. */ + ci.func = (ASMFunction)(void *)(irf->i); } else { /* Need a non-argument register for indirect calls. */ - Reg freg = ra_alloc1(as, ir->op2, RSET_RANGE(RID_R4, RID_R12+1)); + Reg freg = ra_alloc1(as, func, RSET_RANGE(RID_R4, RID_R12+1)); emit_m(as, ARMI_BLXr, freg); ci.func = (ASMFunction)(void *)0; } diff --git a/src/lj_asm_ppc.h b/src/lj_asm_ppc.h index 166cf2e4..196ca2ed 100644 --- a/src/lj_asm_ppc.h +++ b/src/lj_asm_ppc.h @@ -284,6 +284,8 @@ static void asm_gencall(ASMState *as, const CCallInfo *ci, IRRef *args) ofs += 4; } } + if ((ci->flags & CCI_VARARG)) /* Vararg calls need to know about FPR use. */ + emit_tab(as, fpr == REGARG_FIRSTFPR ? PPCI_CRXOR : PPCI_CREQV, 6, 6, 6); } /* Setup result reg/sp for call. Evict scratch regs. */ @@ -336,14 +338,18 @@ static void asm_callx(ASMState *as, IRIns *ir) { IRRef args[CCI_NARGS_MAX]; CCallInfo ci; + IRRef func; + IRIns *irf; ci.flags = asm_callx_flags(as, ir); asm_collectargs(as, ir, &ci, args); asm_setupresult(as, ir, &ci); - if (irref_isk(ir->op2)) { /* Call to constant address. */ - ci.func = (ASMFunction)(void *)(IR(ir->op2)->i); + func = ir->op2; irf = IR(func); + if (irf->o == IR_CARG) { func = irf->op1; irf = IR(func); } + if (irref_isk(func)) { /* Call to constant address. */ + ci.func = (ASMFunction)(void *)(irf->i); } else { /* Need a non-argument register for indirect calls. */ RegSet allow = RSET_GPR & ~RSET_RANGE(RID_R0, REGARG_LASTGPR+1); - Reg freg = ra_alloc1(as, ir->op2, allow); + Reg freg = ra_alloc1(as, func, allow); *--as->mcp = PPCI_BCTRL; *--as->mcp = PPCI_MTCTR | PPCF_T(freg); ci.func = (ASMFunction)(void *)0; diff --git a/src/lj_asm_x86.h b/src/lj_asm_x86.h index 154ca890..391e2de9 100644 --- a/src/lj_asm_x86.h +++ b/src/lj_asm_x86.h @@ -369,18 +369,76 @@ static Reg asm_fuseload(ASMState *as, IRRef ref, RegSet allow) /* -- Calls --------------------------------------------------------------- */ +/* Count the required number of stack slots for a call. */ +static int asm_count_call_slots(ASMState *as, const CCallInfo *ci, IRRef *args) +{ + uint32_t i, nargs = CCI_NARGS(ci); + int nslots = 0; +#if LJ_64 + if (LJ_ABI_WIN) { + nslots = (int)(nargs*2); /* Only matters for more than four args. */ + } else { + int ngpr = REGARG_NUMGPR, nfpr = REGARG_NUMFPR; + for (i = 0; i < nargs; i++) + if (args[i] && irt_isfp(IR(args[i])->t)) { + if (nfpr > 0) nfpr--; else nslots += 2; + } else { + if (ngpr > 0) ngpr--; else nslots += 2; + } + } +#else + int ngpr = 0; + if ((ci->flags & CCI_CC_MASK) == CCI_CC_FASTCALL) + ngpr = 2; + else if ((ci->flags & CCI_CC_MASK) == CCI_CC_THISCALL) + ngpr = 1; + for (i = 0; i < nargs; i++) + if (args[i] && irt_isfp(IR(args[i])->t)) { + nslots += irt_isnum(IR(args[i])->t) ? 2 : 1; + } else { + if (ngpr > 0) ngpr--; else nslots++; + } +#endif + return nslots; +} + /* Generate a call to a C function. */ static void asm_gencall(ASMState *as, const CCallInfo *ci, IRRef *args) { uint32_t n, nargs = CCI_NARGS(ci); int32_t ofs = STACKARG_OFS; - uint32_t gprs = REGARG_GPRS; #if LJ_64 + uint32_t gprs = REGARG_GPRS; Reg fpr = REGARG_FIRSTFPR; +#if !LJ_ABI_WIN + MCode *patchnfpr = NULL; +#endif +#else + uint32_t gprs = 0; + if ((ci->flags & CCI_CC_MASK) != CCI_CC_CDECL) { + if ((ci->flags & CCI_CC_MASK) == CCI_CC_THISCALL) + gprs = (REGARG_GPRS & 31); + else if ((ci->flags & CCI_CC_MASK) == CCI_CC_FASTCALL) + gprs = REGARG_GPRS; + } #endif - lua_assert(!(nargs > 2 && (ci->flags&CCI_FASTCALL))); /* Avoid stack adj. */ if ((void *)ci->func) emit_call(as, ci->func); +#if LJ_64 + if ((ci->flags & CCI_VARARG)) { /* Special handling for vararg calls. */ +#if LJ_ABI_WIN + for (n = 0; n < 4 && n < nargs; n++) { + IRIns *ir = IR(args[n]); + if (irt_isfp(ir->t)) /* Duplicate FPRs in GPRs. */ + emit_rr(as, XO_MOVDto, (irt_isnum(ir->t) ? REX_64 : 0) | (fpr+n), + ((gprs >> (n*5)) & 31)); /* Either MOVD or MOVQ. */ + } +#else + patchnfpr = --as->mcp; /* Indicate number of used FPRs in register al. */ + *--as->mcp = XI_MOVrib | RID_EAX; +#endif + } +#endif for (n = 0; n < nargs; n++) { /* Setup args. */ IRRef ref = args[n]; IRIns *ir = IR(ref); @@ -392,15 +450,16 @@ static void asm_gencall(ASMState *as, const CCallInfo *ci, IRRef *args) #elif LJ_64 /* POSIX/x64 argument registers are used in order of appearance. */ if (irt_isfp(ir->t)) { - r = fpr <= REGARG_LASTFPR ? fpr : 0; fpr++; + r = fpr <= REGARG_LASTFPR ? fpr++ : 0; } else { r = gprs & 31; gprs >>= 5; } #else - if (irt_isfp(ir->t) || !(ci->flags & CCI_FASTCALL)) { + if (ref && irt_isfp(ir->t)) { r = 0; } else { r = gprs & 31; gprs >>= 5; + if (!ref) continue; } #endif if (r) { /* Argument is in a register. */ @@ -442,6 +501,9 @@ static void asm_gencall(ASMState *as, const CCallInfo *ci, IRRef *args) ofs += sizeof(intptr_t); } } +#if LJ_64 && !LJ_ABI_WIN + if (patchnfpr) *patchnfpr = fpr - REGARG_FIRSTFPR; +#endif } /* Setup result reg/sp for call. Evict scratch regs. */ @@ -503,23 +565,50 @@ static void asm_call(ASMState *as, IRIns *ir) asm_gencall(as, ci, args); } +/* Return a constant function pointer or NULL for indirect calls. */ +static void *asm_callx_func(ASMState *as, IRIns *irf, IRRef func) +{ +#if LJ_32 + UNUSED(as); + if (irref_isk(func)) + return (void *)irf->i; +#else + if (irref_isk(func)) { + MCode *p; + if (irf->o == IR_KINT64) + p = (MCode *)(void *)ir_k64(irf)->u64; + else + p = (MCode *)(void *)(uintptr_t)(uint32_t)irf->i; + if (p - as->mcp == (int32_t)(p - as->mcp)) + return p; /* Call target is still in +-2GB range. */ + /* Avoid the indirect case of emit_call(). Try to hoist func addr. */ + } +#endif + return NULL; +} + static void asm_callx(ASMState *as, IRIns *ir) { IRRef args[CCI_NARGS_MAX]; CCallInfo ci; + IRRef func; IRIns *irf; ci.flags = asm_callx_flags(as, ir); asm_collectargs(as, ir, &ci, args); asm_setupresult(as, ir, &ci); - irf = IR(ir->op2); - if (LJ_32 && irref_isk(ir->op2)) { /* Call to constant address on x86. */ - ci.func = (ASMFunction)(void *)(uintptr_t)(uint32_t)irf->i; - } else { - /* Prefer a non-argument register or RID_RET for indirect calls. */ - RegSet allow = (RSET_GPR & ~RSET_SCRATCH)|RID2RSET(RID_RET); - Reg r = ra_alloc1(as, ir->op2, allow); +#if LJ_32 + /* Have to readjust stack after non-cdecl calls due to callee cleanup. */ + if ((ci.flags & CCI_CC_MASK) != CCI_CC_CDECL) + emit_spsub(as, 4 * asm_count_call_slots(as, &ci, args)); +#endif + func = ir->op2; irf = IR(func); + if (irf->o == IR_CARG) { func = irf->op1; irf = IR(func); } + ci.func = (ASMFunction)asm_callx_func(as, irf, func); + if (!(void *)ci.func) { + /* Use a (hoistable) non-scratch register for indirect calls. */ + RegSet allow = (RSET_GPR & ~RSET_SCRATCH); + Reg r = ra_alloc1(as, func, allow); emit_rr(as, XO_GROUP5, XOg_CALL, r); - ci.func = (ASMFunction)(void *)0; } asm_gencall(as, &ci, args); } @@ -2608,35 +2697,14 @@ static void asm_ir(ASMState *as, IRIns *ir) static Reg asm_setup_call_slots(ASMState *as, IRIns *ir, const CCallInfo *ci) { IRRef args[CCI_NARGS_MAX]; - uint32_t nargs = (int)CCI_NARGS(ci); - int nslots = 0; + int nslots; asm_collectargs(as, ir, ci, args); -#if LJ_64 - if (LJ_ABI_WIN) { - nslots = (int)(nargs*2); /* Only matters for more than four args. */ - } else { - uint32_t i; - int ngpr = 6, nfpr = 8; - for (i = 0; i < nargs; i++) - if (args[i] && irt_isfp(IR(args[i])->t)) { - if (nfpr > 0) nfpr--; else nslots += 2; - } else { - if (ngpr > 0) ngpr--; else nslots += 2; - } - } + nslots = asm_count_call_slots(as, ci, args); if (nslots > as->evenspill) /* Leave room for args in stack slots. */ as->evenspill = nslots; +#if LJ_64 return irt_isfp(ir->t) ? REGSP_HINT(RID_FPRET) : REGSP_HINT(RID_RET); #else - if ((ci->flags & CCI_FASTCALL)) { - lua_assert(nargs <= 2); - } else { - uint32_t i; - for (i = 0; i < nargs; i++) - nslots += (args[i] && irt_isnum(IR(args[i])->t)) ? 2 : 1; - if (nslots > as->evenspill) /* Leave room for args. */ - as->evenspill = nslots; - } return irt_isfp(ir->t) ? REGSP_INIT : REGSP_HINT(RID_RET); #endif } diff --git a/src/lj_ccall.c b/src/lj_ccall.c index 281b45a6..c1c04b6f 100644 --- a/src/lj_ccall.c +++ b/src/lj_ccall.c @@ -402,7 +402,7 @@ static void ccall_struct_ret(CCallState *cc, int *rcl, uint8_t *dp, CTSize sz) /* -- Common C call handling ---------------------------------------------- */ /* Infer the destination CTypeID for a vararg argument. */ -static CTypeID ccall_ctid_vararg(CTState *cts, cTValue *o) +CTypeID lj_ccall_ctid_vararg(CTState *cts, cTValue *o) { if (tvisnumber(o)) { return CTID_DOUBLE; @@ -506,7 +506,7 @@ static int ccall_set_args(lua_State *L, CTState *cts, CType *ct, } else { if (!(ct->info & CTF_VARARG)) lj_err_caller(L, LJ_ERR_FFI_NUMARG); /* Too many arguments. */ - did = ccall_ctid_vararg(cts, o); /* Infer vararg type. */ + did = lj_ccall_ctid_vararg(cts, o); /* Infer vararg type. */ isva = 1; } d = ctype_raw(cts, did); diff --git a/src/lj_ccall.h b/src/lj_ccall.h index d9b1e42c..890f665d 100644 --- a/src/lj_ccall.h +++ b/src/lj_ccall.h @@ -7,6 +7,7 @@ #define _LJ_CCALL_H #include "lj_obj.h" +#include "lj_ctype.h" #if LJ_HASFFI @@ -129,6 +130,8 @@ typedef struct CCallState { /* Really belongs to lj_vm.h. */ LJ_ASMF void LJ_FASTCALL lj_vm_ffi_call(CCallState *cc); + +LJ_FUNC CTypeID lj_ccall_ctid_vararg(CTState *cts, cTValue *o); LJ_FUNC int lj_ccall_func(lua_State *L, GCcdata *cd); #endif diff --git a/src/lj_crecord.c b/src/lj_crecord.c index 4e6a644a..3dd6f495 100644 --- a/src/lj_crecord.c +++ b/src/lj_crecord.c @@ -18,6 +18,7 @@ #include "lj_cparse.h" #include "lj_cconv.h" #include "lj_clib.h" +#include "lj_ccall.h" #include "lj_ir.h" #include "lj_jit.h" #include "lj_ircall.h" @@ -364,7 +365,7 @@ static TRef crec_tv_ct(jit_State *J, CType *s, CTypeID sid, TRef sp) /* -- Convert TValue to C type (store) ------------------------------------ */ -static TRef crec_ct_tv(jit_State *J, CType *d, TRef dp, TRef sp, TValue *sval) +static TRef crec_ct_tv(jit_State *J, CType *d, TRef dp, TRef sp, cTValue *sval) { CTState *cts = ctype_ctsG(J2G(J)); CTypeID sid = CTID_P_VOID; @@ -747,29 +748,88 @@ static TRef crec_call_args(jit_State *J, RecordFFData *rd, CTState *cts, CType *ct) { TRef args[CCI_NARGS_MAX]; + CTypeID fid; MSize i, n; - TRef tr; + TRef tr, *base; + cTValue *o; +#if LJ_TARGET_X86 +#if LJ_ABI_WIN + TRef *arg0 = NULL, *arg1 = NULL; +#endif + int ngpr = 0; + if (ctype_cconv(ct->info) == CTCC_THISCALL) + ngpr = 1; + else if (ctype_cconv(ct->info) == CTCC_FASTCALL) + ngpr = 2; +#endif + + /* Skip initial attributes. */ + fid = ct->sib; + while (fid) { + CType *ctf = ctype_get(cts, fid); + if (!ctype_isattrib(ctf->info)) break; + fid = ctf->sib; + } args[0] = TREF_NIL; - for (n = 0; J->base[n+1]; n++) { + for (n = 0, base = J->base+1, o = rd->argv+1; *base; n++, base++, o++) { + CTypeID did; CType *d; - do { - if (!ct->sib || n >= CCI_NARGS_MAX) - lj_trace_err(J, LJ_TRERR_NYICALL); - ct = ctype_get(cts, ct->sib); - } while (ctype_isattrib(ct->info)); - if (!ctype_isfield(ct->info)) + + if (n >= CCI_NARGS_MAX) lj_trace_err(J, LJ_TRERR_NYICALL); - d = ctype_rawchild(cts, ct); + + if (fid) { /* Get argument type from field. */ + CType *ctf = ctype_get(cts, fid); + fid = ctf->sib; + lua_assert(ctype_isfield(ctf->info)); + did = ctype_cid(ctf->info); + } else { + if (!(ct->info & CTF_VARARG)) + lj_trace_err(J, LJ_TRERR_NYICALL); /* Too many arguments. */ + did = lj_ccall_ctid_vararg(cts, o); /* Infer vararg type. */ + } + d = ctype_raw(cts, did); if (!(ctype_isnum(d->info) || ctype_isptr(d->info) || ctype_isenum(d->info))) lj_trace_err(J, LJ_TRERR_NYICALL); - tr = crec_ct_tv(J, d, 0, J->base[n+1], &rd->argv[n+1]); - if (ctype_isinteger_or_bool(d->info) && d->size < 4) { - if ((d->info & CTF_UNSIGNED)) - tr = emitconv(tr, IRT_INT, d->size==1 ? IRT_U8 : IRT_U16, 0); - else - tr = emitconv(tr, IRT_INT, d->size==1 ? IRT_I8 : IRT_I16, IRCONV_SEXT); + tr = crec_ct_tv(J, d, 0, *base, o); + if (ctype_isinteger_or_bool(d->info)) { + if (d->size < 4) { + if ((d->info & CTF_UNSIGNED)) + tr = emitconv(tr, IRT_INT, d->size==1 ? IRT_U8 : IRT_U16, 0); + else + tr = emitconv(tr, IRT_INT, d->size==1 ? IRT_I8 : IRT_I16,IRCONV_SEXT); + } } +#if LJ_TARGET_X86 + /* 64 bit args must not end up in registers for fastcall/thiscall. */ +#if LJ_ABI_WIN + if (!ctype_isfp(d->info)) { + /* Sigh, the Windows/x86 ABI allows reordering across 64 bit args. */ + if (tref_typerange(tr, IRT_I64, IRT_U64)) { + if (ngpr) { + arg0 = &args[n]; args[n++] = TREF_NIL; ngpr--; + if (ngpr) { + arg1 = &args[n]; args[n++] = TREF_NIL; ngpr--; + } + } + } else { + if (arg0) { *arg0 = tr; arg0 = NULL; n--; continue; } + if (arg1) { *arg1 = tr; arg1 = NULL; n--; continue; } + if (ngpr) ngpr--; + } + } +#else + if (!ctype_isfp(d->info) && ngpr) { + if (tref_typerange(tr, IRT_I64, IRT_U64)) { + /* No reordering for other x86 ABIs. Simply add alignment args. */ + do { args[n++] = TREF_NIL; } while (--ngpr); + } else { + ngpr--; + } + } +#endif +#endif args[n] = tr; } tr = args[0]; @@ -801,12 +861,15 @@ static int crec_call(jit_State *J, RecordFFData *rd, GCcdata *cd) } if (!(ctype_isnum(ctr->info) || ctype_isptr(ctr->info) || ctype_isvoid(ctr->info)) || - ctype_isbool(ctr->info) || (ct->info & CTF_VARARG) || -#if LJ_TARGET_X86 - ctype_cconv(ct->info) != CTCC_CDECL || -#endif - t == IRT_CDATA) + ctype_isbool(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 +#endif + ) + func = emitir(IRT(IR_CARG, IRT_NIL), func, + lj_ir_kint(J, ctype_typeid(cts, ct))); tr = emitir(IRT(IR_CALLXS, t), crec_call_args(J, rd, cts, ct), func); if (t == IRT_FLOAT || t == IRT_U32) { tr = emitconv(tr, IRT_NUM, t, 0); diff --git a/src/lj_ctype.h b/src/lj_ctype.h index 82c4427a..49f28108 100644 --- a/src/lj_ctype.h +++ b/src/lj_ctype.h @@ -117,7 +117,7 @@ LJ_STATIC_ASSERT(((int)CT_STRUCT & (int)CT_ARRAY) == CT_STRUCT); info = (info & ~(CTMASK_##field<