String buffers, part 3d: Compile string buffer methods and functions.

Sponsored by fmad.io.
This commit is contained in:
Mike Pall
2021-07-19 16:53:30 +02:00
parent 2182630903
commit 29bc1f04ac
18 changed files with 625 additions and 87 deletions

View File

@@ -11,6 +11,7 @@
#if LJ_HASJIT
#include "lj_err.h"
#include "lj_buf.h"
#include "lj_str.h"
#include "lj_tab.h"
#include "lj_frame.h"
@@ -28,6 +29,7 @@
#include "lj_vm.h"
#include "lj_strscan.h"
#include "lj_strfmt.h"
#include "lj_serialize.h"
/* Some local macros to save typing. Undef'd at the end. */
#define IR(ref) (&J->cur.ir[(ref)])
@@ -941,20 +943,18 @@ static void LJ_FASTCALL recff_string_find(jit_State *J, RecordFFData *rd)
}
}
static void LJ_FASTCALL recff_string_format(jit_State *J, RecordFFData *rd)
static void recff_format(jit_State *J, RecordFFData *rd, TRef hdr, int sbufx)
{
TRef trfmt = lj_ir_tostr(J, J->base[0]);
GCstr *fmt = argv2str(J, &rd->argv[0]);
int arg = 1;
TRef hdr, tr;
ptrdiff_t arg = sbufx;
TRef tr = hdr, trfmt = lj_ir_tostr(J, J->base[arg]);
GCstr *fmt = argv2str(J, &rd->argv[arg]);
FormatState fs;
SFormat sf;
/* Specialize to the format string. */
emitir(IRTG(IR_EQ, IRT_STR), trfmt, lj_ir_kstr(J, fmt));
tr = hdr = recff_bufhdr(J);
lj_strfmt_init(&fs, strdata(fmt), fmt->len);
while ((sf = lj_strfmt_parse(&fs)) != STRFMT_EOF) { /* Parse format. */
TRef tra = sf == STRFMT_LIT ? 0 : J->base[arg++];
TRef tra = sf == STRFMT_LIT ? 0 : J->base[++arg];
TRef trsf = lj_ir_kint(J, (int32_t)sf);
IRCallID id;
switch (STRFMT_TYPE(sf)) {
@@ -968,9 +968,8 @@ static void LJ_FASTCALL recff_string_format(jit_State *J, RecordFFData *rd)
if (!tref_isinteger(tra)) {
#if LJ_HASFFI
if (tref_iscdata(tra)) {
tra = lj_crecord_loadiu64(J, tra, &rd->argv[arg-1]);
tra = lj_crecord_loadiu64(J, tra, &rd->argv[arg]);
tr = lj_ir_call(J, IRCALL_lj_strfmt_putfxint, tr, trsf, tra);
lj_needsplit(J);
break;
}
#endif
@@ -1004,6 +1003,7 @@ static void LJ_FASTCALL recff_string_format(jit_State *J, RecordFFData *rd)
case STRFMT_STR:
if (!tref_isstr(tra)) {
recff_nyiu(J, rd); /* NYI: __tostring and non-string types for %s. */
/* NYI: also buffers. */
return;
}
if (sf == STRFMT_STR) /* Shortcut for plain %s. */
@@ -1028,9 +1028,334 @@ static void LJ_FASTCALL recff_string_format(jit_State *J, RecordFFData *rd)
return;
}
}
J->base[0] = emitir(IRTG(IR_BUFSTR, IRT_STR), tr, hdr);
if (sbufx) {
emitir(IRT(IR_USE, IRT_NIL), tr, 0);
} else {
J->base[0] = emitir(IRTG(IR_BUFSTR, IRT_STR), tr, hdr);
}
}
static void LJ_FASTCALL recff_string_format(jit_State *J, RecordFFData *rd)
{
recff_format(J, rd, recff_bufhdr(J), 0);
}
/* -- Buffer library fast functions --------------------------------------- */
#if LJ_HASBUFFER
static LJ_AINLINE TRef recff_sbufx_get_L(jit_State *J, TRef ud)
{
return emitir(IRT(IR_FLOAD, IRT_PGC), ud, IRFL_SBUF_L);
}
static LJ_AINLINE void recff_sbufx_set_L(jit_State *J, TRef ud, TRef val)
{
TRef fref = emitir(IRT(IR_FREF, IRT_PGC), ud, IRFL_SBUF_L);
emitir(IRT(IR_FSTORE, IRT_PGC), fref, val);
}
static LJ_AINLINE TRef recff_sbufx_get_ptr(jit_State *J, TRef ud, IRFieldID fl)
{
return emitir(IRT(IR_FLOAD, IRT_PTR), ud, fl);
}
static LJ_AINLINE void recff_sbufx_set_ptr(jit_State *J, TRef ud, IRFieldID fl, TRef val)
{
TRef fref = emitir(IRT(IR_FREF, IRT_PTR), ud, fl);
emitir(IRT(IR_FSTORE, IRT_PTR), fref, val);
}
static LJ_AINLINE TRef recff_sbufx_len(jit_State *J, TRef trr, TRef trw)
{
TRef len = emitir(IRT(IR_SUB, IRT_INTP), trw, trr);
if (LJ_64)
len = emitir(IRTI(IR_CONV), len, (IRT_INT<<5)|IRT_INTP|IRCONV_NONE);
return len;
}
/* Emit typecheck for string buffer. */
static TRef recff_sbufx_check(jit_State *J, RecordFFData *rd, int arg)
{
TRef trtype, ud = J->base[arg];
if (!tvisbuf(&rd->argv[arg])) lj_trace_err(J, LJ_TRERR_BADTYPE);
trtype = emitir(IRT(IR_FLOAD, IRT_U8), ud, IRFL_UDATA_UDTYPE);
emitir(IRTGI(IR_EQ), trtype, lj_ir_kint(J, UDTYPE_BUFFER));
return ud;
}
/* Emit BUFHDR for write to extended string buffer. */
static TRef recff_sbufx_write(jit_State *J, TRef ud)
{
TRef trbuf = emitir(IRT(IR_ADD, IRT_PGC), ud, lj_ir_kint(J, sizeof(GCudata)));
return emitir(IRT(IR_BUFHDR, IRT_PGC), trbuf, IRBUFHDR_WRITE);
}
/* Check for integer in range for the buffer API. */
static TRef recff_sbufx_checkint(jit_State *J, RecordFFData *rd, int arg)
{
TRef tr = J->base[arg];
TRef trlim = lj_ir_kint(J, LJ_MAX_BUF);
if (tref_isinteger(tr)) {
emitir(IRTGI(IR_ULE), tr, trlim);
} else if (tref_isnum(tr)) {
tr = emitir(IRTI(IR_CONV), tr, IRCONV_INT_NUM|IRCONV_ANY);
emitir(IRTGI(IR_ULE), tr, trlim);
#if LJ_HASFFI
} else if (tref_iscdata(tr)) {
tr = lj_crecord_loadiu64(J, tr, &rd->argv[arg]);
emitir(IRTG(IR_ULE, IRT_U64), tr, lj_ir_kint64(J, LJ_MAX_BUF));
tr = emitir(IRTI(IR_CONV), tr, (IRT_INT<<5)|IRT_I64|IRCONV_NONE);
#else
UNUSED(rd);
#endif
} else {
lj_trace_err(J, LJ_TRERR_BADTYPE);
}
return tr;
}
static void LJ_FASTCALL recff_buffer_method_reset(jit_State *J, RecordFFData *rd)
{
TRef ud = recff_sbufx_check(J, rd, 0);
SBufExt *sbx = bufV(&rd->argv[0]);
int iscow = (int)sbufiscow(sbx);
TRef trl = recff_sbufx_get_L(J, ud);
TRef trcow = emitir(IRT(IR_BAND, IRT_IGC), trl, lj_ir_kint(J, SBUF_FLAG_COW));
TRef zero = lj_ir_kint(J, 0);
emitir(IRTG(iscow ? IR_NE : IR_EQ, IRT_IGC), trcow, zero);
if (iscow) {
trl = emitir(IRT(IR_BXOR, IRT_IGC), trl,
LJ_GC64 ? lj_ir_kint64(J, SBUF_FLAG_COW) :
lj_ir_kint(J, SBUF_FLAG_COW));
recff_sbufx_set_ptr(J, ud, IRFL_SBUF_W, zero);
recff_sbufx_set_ptr(J, ud, IRFL_SBUF_E, zero);
recff_sbufx_set_ptr(J, ud, IRFL_SBUF_B, zero);
recff_sbufx_set_L(J, ud, trl);
emitir(IRT(IR_FSTORE, IRT_PGC),
emitir(IRT(IR_FREF, IRT_PGC), ud, IRFL_SBUF_REF), zero);
recff_sbufx_set_ptr(J, ud, IRFL_SBUF_R, zero);
} else {
TRef trb = recff_sbufx_get_ptr(J, ud, IRFL_SBUF_B);
recff_sbufx_set_ptr(J, ud, IRFL_SBUF_W, trb);
recff_sbufx_set_ptr(J, ud, IRFL_SBUF_R, trb);
}
}
static void LJ_FASTCALL recff_buffer_method_skip(jit_State *J, RecordFFData *rd)
{
TRef ud = recff_sbufx_check(J, rd, 0);
TRef trr = recff_sbufx_get_ptr(J, ud, IRFL_SBUF_R);
TRef trw = recff_sbufx_get_ptr(J, ud, IRFL_SBUF_W);
TRef len = recff_sbufx_len(J, trr, trw);
TRef trn = recff_sbufx_checkint(J, rd, 1);
len = emitir(IRTI(IR_MIN), len, trn);
trr = emitir(IRT(IR_ADD, IRT_PTR), trr, len);
recff_sbufx_set_ptr(J, ud, IRFL_SBUF_R, trr);
}
static void LJ_FASTCALL recff_buffer_method_set(jit_State *J, RecordFFData *rd)
{
TRef ud = recff_sbufx_check(J, rd, 0);
TRef trbuf = recff_sbufx_write(J, ud);
TRef tr = J->base[1];
if (tref_isstr(tr)) {
TRef trp = emitir(IRT(IR_STRREF, IRT_PGC), tr, lj_ir_kint(J, 0));
TRef len = emitir(IRTI(IR_FLOAD), tr, IRFL_STR_LEN);
lj_ir_call(J, IRCALL_lj_bufx_set, trbuf, trp, len, tr);
#if LJ_HASFFI
} else if (tref_iscdata(tr)) {
TRef trp = lj_crecord_topcvoid(J, tr, &rd->argv[1]);
TRef len = recff_sbufx_checkint(J, rd, 2);
lj_ir_call(J, IRCALL_lj_bufx_set, trbuf, trp, len, tr);
#endif
} /* else: Interpreter will throw. */
}
static void LJ_FASTCALL recff_buffer_method_put(jit_State *J, RecordFFData *rd)
{
TRef ud = recff_sbufx_check(J, rd, 0);
TRef trbuf = recff_sbufx_write(J, ud);
TRef tr;
ptrdiff_t arg;
if (!J->base[1]) return;
for (arg = 1; (tr = J->base[arg]); arg++) {
if (tref_isstr(tr)) {
trbuf = emitir(IRTG(IR_BUFPUT, IRT_PGC), trbuf, tr);
} else if (tref_isnumber(tr)) {
trbuf = emitir(IRTG(IR_BUFPUT, IRT_PGC), trbuf,
emitir(IRT(IR_TOSTR, IRT_STR), tr,
tref_isnum(tr) ? IRTOSTR_NUM : IRTOSTR_INT));
} else if (tref_isudata(tr)) {
TRef ud2 = recff_sbufx_check(J, rd, arg);
TRef trr = recff_sbufx_get_ptr(J, ud2, IRFL_SBUF_R);
TRef trw = recff_sbufx_get_ptr(J, ud2, IRFL_SBUF_W);
TRef len = recff_sbufx_len(J, trr, trw);
emitir(IRTG(IR_NE, IRT_PGC), ud, ud2);
trbuf = lj_ir_call(J, IRCALL_lj_buf_putmem, trbuf, trr, len);
} else {
recff_nyiu(J, rd);
}
}
emitir(IRT(IR_USE, IRT_NIL), trbuf, 0);
}
static void LJ_FASTCALL recff_buffer_method_putf(jit_State *J, RecordFFData *rd)
{
TRef ud = recff_sbufx_check(J, rd, 0);
TRef trbuf = recff_sbufx_write(J, ud);
recff_format(J, rd, trbuf, 1);
}
static void LJ_FASTCALL recff_buffer_method_get(jit_State *J, RecordFFData *rd)
{
TRef ud = recff_sbufx_check(J, rd, 0);
TRef trr = recff_sbufx_get_ptr(J, ud, IRFL_SBUF_R);
TRef trw = recff_sbufx_get_ptr(J, ud, IRFL_SBUF_W);
TRef tr;
ptrdiff_t arg;
if (!J->base[1]) { J->base[1] = TREF_NIL; J->base[2] = 0; }
for (arg = 0; (tr = J->base[arg+1]); arg++) {
TRef len = recff_sbufx_len(J, trr, trw);
if (tref_isnil(tr)) {
J->base[arg] = emitir(IRT(IR_XSNEW, IRT_STR), trr, len);
trr = trw;
} else {
TRef trn = recff_sbufx_checkint(J, rd, arg+1);
TRef tru;
len = emitir(IRTI(IR_MIN), len, trn);
tru = emitir(IRT(IR_ADD, IRT_PTR), trr, len);
J->base[arg] = emitir(IRT(IR_XSNEW, IRT_STR), trr, len);
trr = tru; /* Doing the ADD before the SNEW generates better code. */
}
recff_sbufx_set_ptr(J, ud, IRFL_SBUF_R, trr);
}
rd->nres = arg;
}
static void LJ_FASTCALL recff_buffer_method___tostring(jit_State *J, RecordFFData *rd)
{
TRef ud = recff_sbufx_check(J, rd, 0);
TRef trr = recff_sbufx_get_ptr(J, ud, IRFL_SBUF_R);
TRef trw = recff_sbufx_get_ptr(J, ud, IRFL_SBUF_W);
J->base[0] = emitir(IRT(IR_XSNEW, IRT_STR), trr, recff_sbufx_len(J, trr, trw));
}
static void LJ_FASTCALL recff_buffer_method___len(jit_State *J, RecordFFData *rd)
{
TRef ud = recff_sbufx_check(J, rd, 0);
TRef trr = recff_sbufx_get_ptr(J, ud, IRFL_SBUF_R);
TRef trw = recff_sbufx_get_ptr(J, ud, IRFL_SBUF_W);
J->base[0] = recff_sbufx_len(J, trr, trw);
}
#if LJ_HASFFI
static void LJ_FASTCALL recff_buffer_method_putcdata(jit_State *J, RecordFFData *rd)
{
TRef ud = recff_sbufx_check(J, rd, 0);
TRef trbuf = recff_sbufx_write(J, ud);
TRef tr = lj_crecord_topcvoid(J, J->base[1], &rd->argv[1]);
TRef len = recff_sbufx_checkint(J, rd, 2);
trbuf = lj_ir_call(J, IRCALL_lj_buf_putmem, trbuf, tr, len);
emitir(IRT(IR_USE, IRT_NIL), trbuf, 0);
}
static void LJ_FASTCALL recff_buffer_method_reserve(jit_State *J, RecordFFData *rd)
{
TRef ud = recff_sbufx_check(J, rd, 0);
TRef trbuf = recff_sbufx_write(J, ud);
TRef trsz = recff_sbufx_checkint(J, rd, 1);
J->base[1] = lj_ir_call(J, IRCALL_lj_bufx_more, trbuf, trsz);
J->base[0] = lj_crecord_topuint8(J, recff_sbufx_get_ptr(J, ud, IRFL_SBUF_W));
rd->nres = 2;
}
static void LJ_FASTCALL recff_buffer_method_commit(jit_State *J, RecordFFData *rd)
{
TRef ud = recff_sbufx_check(J, rd, 0);
TRef len = recff_sbufx_checkint(J, rd, 1);
TRef trw = recff_sbufx_get_ptr(J, ud, IRFL_SBUF_W);
TRef tre = recff_sbufx_get_ptr(J, ud, IRFL_SBUF_E);
TRef left = emitir(IRT(IR_SUB, IRT_INTP), tre, trw);
if (LJ_64)
left = emitir(IRTI(IR_CONV), left, (IRT_INT<<5)|IRT_INTP|IRCONV_NONE);
emitir(IRTGI(IR_ULE), len, left);
trw = emitir(IRT(IR_ADD, IRT_PTR), trw, len);
recff_sbufx_set_ptr(J, ud, IRFL_SBUF_W, trw);
}
static void LJ_FASTCALL recff_buffer_method_ref(jit_State *J, RecordFFData *rd)
{
TRef ud = recff_sbufx_check(J, rd, 0);
TRef trr = recff_sbufx_get_ptr(J, ud, IRFL_SBUF_R);
TRef trw = recff_sbufx_get_ptr(J, ud, IRFL_SBUF_W);
J->base[0] = lj_crecord_topuint8(J, trr);
J->base[1] = recff_sbufx_len(J, trr, trw);
rd->nres = 2;
}
#endif
static void LJ_FASTCALL recff_buffer_method_encode(jit_State *J, RecordFFData *rd)
{
TRef ud = recff_sbufx_check(J, rd, 0);
TRef trbuf = recff_sbufx_write(J, ud);
TRef tmp, tr = J->base[1];
if (!LJ_DUALNUM && tref_isinteger(tr))
tr = emitir(IRTN(IR_CONV), tr, IRCONV_NUM_INT);
tmp = emitir(IRT(IR_TMPREF, IRT_PGC), tr, IRTMPREF_IN1);
lj_ir_call(J, IRCALL_lj_serialize_put, trbuf, tmp);
/* No IR_USE needed, since the call is a store. */
}
static void LJ_FASTCALL recff_buffer_method_decode(jit_State *J, RecordFFData *rd)
{
TRef ud = recff_sbufx_check(J, rd, 0);
TRef trbuf = recff_sbufx_write(J, ud);
TRef trr, tmp;
IRType t;
tmp = emitir(IRT(IR_TMPREF, IRT_PGC), REF_NIL, IRTMPREF_OUT1);
trr = lj_ir_call(J, IRCALL_lj_serialize_get, trbuf, tmp);
/* No IR_USE needed, since the call is a store. */
t = (IRType)lj_serialize_peektype(bufV(&rd->argv[0]));
J->base[0] = lj_record_vload(J, tmp, t);
/* The sbx->r store must be after the VLOAD type check, in case it fails. */
recff_sbufx_set_ptr(J, ud, IRFL_SBUF_R, trr);
}
static void LJ_FASTCALL recff_buffer_encode(jit_State *J, RecordFFData *rd)
{
TRef tmp, tr = J->base[0];
if (!LJ_DUALNUM && tref_isinteger(tr))
tr = emitir(IRTN(IR_CONV), tr, IRCONV_NUM_INT);
tmp = emitir(IRT(IR_TMPREF, IRT_PGC), tr, IRTMPREF_IN1);
J->base[0] = lj_ir_call(J, IRCALL_lj_serialize_encode, tmp);
/* IR_USE needed for IR_CALLA, because the encoder may throw non-OOM. */
emitir(IRT(IR_USE, IRT_NIL), J->base[0], 0);
UNUSED(rd);
}
static void LJ_FASTCALL recff_buffer_decode(jit_State *J, RecordFFData *rd)
{
if (tvisstr(&rd->argv[0])) {
GCstr *str = strV(&rd->argv[0]);
SBufExt sbx;
TRef tr, tmp;
IRType t;
tmp = emitir(IRT(IR_TMPREF, IRT_PGC), REF_NIL, IRTMPREF_OUT1);
tr = lj_ir_call(J, IRCALL_lj_serialize_decode, tmp, J->base[0]);
/* IR_USE needed for IR_CALLA, because the decoder may throw non-OOM.
** That's why IRCALL_lj_serialize_decode needs a fake INT result.
*/
emitir(IRT(IR_USE, IRT_NIL), tr, 0);
memset(&sbx, 0, sizeof(SBufExt));
lj_bufx_set_cow(J->L, &sbx, strdata(str), str->len);
t = (IRType)lj_serialize_peektype(&sbx);
J->base[0] = lj_record_vload(J, tmp, t);
} /* else: Interpreter will throw. */
}
#endif
/* -- Table library fast functions ---------------------------------------- */
static void LJ_FASTCALL recff_table_insert(jit_State *J, RecordFFData *rd)