Compile tonumber calls with bases other than 10

This commit is contained in:
Jude Melton-Houghton 2022-09-14 11:44:45 -04:00
parent dad04f1754
commit 9ea9e9403a
5 changed files with 73 additions and 25 deletions

View File

@ -289,31 +289,10 @@ LJLIB_ASM(tonumber) LJLIB_REC(.)
}
#endif
} else {
const char *p = strdata(lj_lib_checkstr(L, 1));
char *ep;
unsigned int neg = 0;
unsigned long ul;
if (base < 2 || base > 36)
lj_err_arg(L, 2, LJ_ERR_BASERNG);
while (lj_char_isspace((unsigned char)(*p))) p++;
if (*p == '-') { p++; neg = 1; } else if (*p == '+') { p++; }
if (lj_char_isalnum((unsigned char)(*p))) {
ul = strtoul(p, &ep, base);
if (p != ep) {
while (lj_char_isspace((unsigned char)(*ep))) ep++;
if (*ep == '\0') {
if (LJ_DUALNUM && LJ_LIKELY(ul < 0x80000000u+neg)) {
if (neg) ul = (unsigned long)-(long)ul;
setintV(L->base-1-LJ_FR2, (int32_t)ul);
} else {
lua_Number n = (lua_Number)ul;
if (neg) n = -n;
setnumV(L->base-1-LJ_FR2, n);
}
return FFH_RES(1);
}
}
}
if (lj_strscan_number_base(lj_lib_checkstr(L, 1), L->base-1-LJ_FR2, base))
return FFH_RES(1);
}
setnilV(L->base-1-LJ_FR2);
return FFH_RES(1);

View File

@ -349,9 +349,28 @@ static void LJ_FASTCALL recff_tonumber(jit_State *J, RecordFFData *rd)
TRef tr = J->base[0];
TRef base = J->base[1];
if (tr && !tref_isnil(base)) {
int32_t kbase;
base = lj_opt_narrow_toint(J, base);
if (!tref_isk(base) || IR(tref_ref(base))->i != 10) {
recff_nyiu(J, rd);
if (!tref_isk(base) || (kbase = IR(tref_ref(base))->i) != 10) {
if (tref_isstr(tr)) {
if (tref_isk(tr) && tref_isk(base) && kbase >= 2 && kbase <= 36) {
TValue num;
if (lj_strscan_num_base(strV(&rd->argv[0]), &num, (int)kbase))
J->base[0] = lj_ir_knum(J, numV(&num));
else
J->base[0] = TREF_NIL;
} else {
TRef tmp, parsed;
emitir(IRTGI(IR_GE), base, lj_ir_kint(J, 2));
emitir(IRTGI(IR_LE), base, lj_ir_kint(J, 36));
tmp = recff_tmpref(J, TREF_NIL, IRTMPREF_OUT1);
parsed = lj_ir_call(J, IRCALL_lj_strscan_num_base, tr, tmp, base);
emitir(IRTGI(IR_NE), parsed, lj_ir_kint(J, 0));
J->base[0] = lj_record_vload(J, tmp, 0, IRT_NUM);
}
} else {
recff_nyiu(J, rd);
}
return;
}
}

View File

@ -155,6 +155,7 @@ typedef struct CCallInfo {
_(ANY, lj_str_find, 4, N, PGC, 0) \
_(ANY, lj_str_new, 3, S, STR, CCI_L|CCI_T) \
_(ANY, lj_strscan_num, 2, FN, INT, 0) \
_(ANY, lj_strscan_num_base, 3, N, INT, 0) \
_(ANY, lj_strfmt_int, 2, FN, STR, CCI_L|CCI_T) \
_(ANY, lj_strfmt_num, 2, FN, STR, CCI_L|CCI_T) \
_(ANY, lj_strfmt_char, 2, FN, STR, CCI_L|CCI_T) \

View File

@ -552,6 +552,46 @@ int LJ_FASTCALL lj_strscan_number(GCstr *str, TValue *o)
}
#endif
static int strscan_base(GCstr *str, TValue *o, int base, int dualnum)
{
const char *p = strdata(str);
char *ep;
unsigned int neg = 0;
unsigned long ul;
while (lj_char_isspace((unsigned char)(*p))) p++;
if (*p == '-') { p++; neg = 1; } else if (*p == '+') { p++; }
if (lj_char_isalnum((unsigned char)(*p))) {
ul = strtoul(p, &ep, base);
if (p != ep) {
while (lj_char_isspace((unsigned char)(*ep))) ep++;
if (*ep == '\0') {
if (dualnum && LJ_LIKELY(ul < 0x80000000u+neg)) {
if (neg) ul = (unsigned long)-(long)ul;
setintV(o, (int32_t)ul);
} else {
lua_Number n = (lua_Number)ul;
if (neg) n = -n;
setnumV(o, n);
}
return 1;
}
}
}
return 0;
}
LJ_FUNC int lj_strscan_num_base(GCstr *str, TValue *o, int base)
{
return strscan_base(str, o, base, 0);
}
#if LJ_DUALNUM
LJ_FUNC int lj_strscan_number_base(GCstr *str, TValue *o, int base)
{
return strscan_base(str, o, base, 1);
}
#endif
#undef DNEXT
#undef DPREV
#undef DLEN

View File

@ -37,4 +37,13 @@ static LJ_AINLINE int lj_strscan_numberobj(TValue *o)
return tvisnumber(o) || (tvisstr(o) && lj_strscan_number(strV(o), o));
}
/* The base must be between 2 and 36. */
LJ_FUNC int lj_strscan_num_base(GCstr *str, TValue *o, int base);
#if LJ_DUALNUM
LJ_FUNC int lj_strscan_number_base(GCstr *str, TValue *o, int base);
#else
#define lj_strscan_number_base(s, o, base) \
lj_strscan_num_base((s), (o), (base))
#endif
#endif