diff --git a/doc/ext_ffi_semantics.html b/doc/ext_ffi_semantics.html
index d9aa27c8..b612dbc5 100644
--- a/doc/ext_ffi_semantics.html
+++ b/doc/ext_ffi_semantics.html
@@ -980,8 +980,6 @@ two.
Calls to non-cdecl or vararg C functions.
Calls to C functions with aggregates passed or returned by
value.
-Calls to C functions with 64 bit arguments or return values
-on 32 bit CPUs.
Calls to ctype metamethods which are not plain functions.
ctype __newindex tables and non-string lookups in ctype
__index tables.
diff --git a/src/lj_asm.c b/src/lj_asm.c
index d5e74185..46142f5c 100644
--- a/src/lj_asm.c
+++ b/src/lj_asm.c
@@ -3346,6 +3346,7 @@ static void asm_hiop(ASMState *as, IRIns *ir)
break;
}
case IR_CALLN:
+ case IR_CALLXS:
ra_destreg(as, ir, RID_RETHI);
if (!uselo)
ra_allocref(as, ir->op1, RID2RSET(RID_RET)); /* Mark call as used. */
diff --git a/src/lj_crecord.c b/src/lj_crecord.c
index 8330faaf..9c93a6f1 100644
--- a/src/lj_crecord.c
+++ b/src/lj_crecord.c
@@ -785,15 +785,16 @@ static int crec_call(jit_State *J, RecordFFData *rd, GCcdata *cd)
#if LJ_TARGET_X86
ctype_cconv(ct->info) != CTCC_CDECL ||
#endif
- t == IRT_CDATA || (LJ_32 && (t == IRT_I64 || t == IRT_U64)))
+ t == IRT_CDATA)
lj_trace_err(J, LJ_TRERR_NYICALL);
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);
} else if (t == IRT_PTR || (LJ_64 && t == IRT_P32) ||
- (LJ_64 && (t == IRT_I64 || t == IRT_U64))) {
+ (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;
diff --git a/src/lj_opt_split.c b/src/lj_opt_split.c
index 90b2b49c..e52ddfd4 100644
--- a/src/lj_opt_split.c
+++ b/src/lj_opt_split.c
@@ -256,6 +256,8 @@ static void split_ir(jit_State *J)
}
break;
}
+ case IR_CALLXS:
+ goto split_call;
case IR_PHI: {
IRRef hiref2;
if ((irref_isk(nir->op1) && irref_isk(nir->op2)) ||
@@ -285,6 +287,42 @@ static void split_ir(jit_State *J)
nir->op1 = nir->op2 = 0;
}
}
+ } else if (ir->o == IR_CALLXS) {
+ IRRef hiref;
+ split_call:
+ hiref = hisubst[ir->op1];
+ if (hiref) {
+ IROpT ot = nir->ot;
+ IRRef op2 = nir->op2;
+ nir->ot = IRT(IR_CARG, IRT_NIL);
+#if LJ_LE
+ nir->op2 = hiref;
+#else
+ nir->op2 = nir->op1; nir->op1 = hiref;
+#endif
+ ir->prev = nref = split_emit(J, ot, nref, op2);
+ }
+ if (irt_isint64(ir->t))
+ hi = split_emit(J, IRTI(IR_HIOP), nref, nref);
+ } else if (ir->o == IR_CARG) {
+ IRRef hiref = hisubst[ir->op1];
+ if (hiref) {
+ IRRef op2 = nir->op2;
+#if LJ_LE
+ nir->op2 = hiref;
+#else
+ nir->op2 = nir->op1; nir->op1 = hiref;
+#endif
+ ir->prev = nref = split_emit(J, IRT(IR_CARG, IRT_NIL), nref, op2);
+ nir = IR(nref);
+ }
+ hiref = hisubst[ir->op2];
+ if (hiref) {
+#if LJ_BE
+ IRRef tmp = nir->op2; nir->op2 = hiref; hiref = tmp;
+#endif
+ ir->prev = split_emit(J, IRT(IR_CARG, IRT_NIL), nref, hiref);
+ }
} else if (ir->o == IR_CNEWI) {
if (hisubst[ir->op2])
split_emit(J, IRT(IR_HIOP, IRT_NIL), nref, hisubst[ir->op2]);