Refactor string.rep().

master
Mike Pall 2013-04-26 19:57:25 +02:00
parent a98e6a70c1
commit 9b8db403f2
8 changed files with 39 additions and 184 deletions

View File

@ -85,53 +85,23 @@ LJLIB_ASM(string_sub) LJLIB_REC(string_range 1)
return FFH_RETRY;
}
LJLIB_ASM(string_rep)
LJLIB_CF(string_rep)
{
GCstr *s = lj_lib_checkstr(L, 1);
int32_t k = lj_lib_checkint(L, 2);
int32_t rep = lj_lib_checkint(L, 2);
GCstr *sep = lj_lib_optstr(L, 3);
int32_t len = (int32_t)s->len;
global_State *g = G(L);
int64_t tlen;
if (k <= 0) {
empty:
setstrV(L, L->base-1, &g->strempty);
return FFH_RES(1);
SBuf *sb = lj_buf_tmp_(L);
if (sep && rep > 1) {
GCstr *s2 = lj_buf_cat2str(L, sep, s);
lj_buf_reset(sb);
lj_buf_putstr(sb, s);
s = s2;
rep--;
}
if (sep) {
tlen = (int64_t)len + sep->len;
if (tlen > LJ_MAX_STR)
lj_err_caller(L, LJ_ERR_STROV);
tlen *= k;
if (tlen > LJ_MAX_STR)
lj_err_caller(L, LJ_ERR_STROV);
} else {
tlen = (int64_t)k * len;
if (tlen > LJ_MAX_STR)
lj_err_caller(L, LJ_ERR_STROV);
}
if (tlen == 0) {
goto empty;
} else {
char *buf = lj_buf_tmp(L, (MSize)tlen), *p = buf;
const char *src = strdata(s);
if (sep) {
tlen -= sep->len; /* Ignore trailing separator. */
if (k > 1) { /* Paste one string and one separator. */
int32_t i;
i = 0; while (i < len) *p++ = src[i++];
src = strdata(sep); len = sep->len;
i = 0; while (i < len) *p++ = src[i++];
src = buf; len += s->len; k--; /* Now copy that k-1 times. */
}
}
do {
int32_t i = 0;
do { *p++ = src[i++]; } while (i < len);
} while (--k > 0);
setstrV(L, L->base-1, lj_str_new(L, buf, (size_t)tlen));
}
return FFH_RES(1);
sb = lj_buf_putstr_rep(sb, s, rep);
setstrV(L, L->top-1, lj_buf_str(L, sb));
lj_gc_check(L);
return 1;
}
LJLIB_ASM(string_reverse) LJLIB_REC(string_op IRCALL_lj_buf_putstr_reverse)

View File

@ -144,6 +144,30 @@ SBuf * LJ_FASTCALL lj_buf_putstr_upper(SBuf *sb, GCstr *s)
return sb;
}
SBuf *lj_buf_putstr_rep(SBuf *sb, GCstr *s, int32_t rep)
{
MSize len = s->len;
if (rep > 0 && len) {
uint64_t tlen = (uint64_t)rep * len;
char *p;
if (LJ_UNLIKELY(tlen > LJ_MAX_STR))
lj_err_mem(sbufL(sb));
p = lj_buf_more(sb, (MSize)tlen);
if (len == 1) { /* Optimize a common case. */
uint32_t c = strdata(s)[0];
do { *p++ = c; } while (--rep > 0);
} else {
const char *e = strdata(s) + len;
do {
const char *q = strdata(s);
do { *p++ = *q++; } while (q < e);
} while (--rep > 0);
}
setsbufP(sb, p);
}
return sb;
}
GCstr * LJ_FASTCALL lj_buf_tostr(SBuf *sb)
{
return lj_str_new(sbufL(sb), sbufB(sb), sbuflen(sb));

View File

@ -35,6 +35,7 @@ LJ_FUNC SBuf * LJ_FASTCALL lj_buf_putnum(SBuf *sb, cTValue *o);
LJ_FUNCA SBuf * LJ_FASTCALL lj_buf_putstr_reverse(SBuf *sb, GCstr *s);
LJ_FUNCA SBuf * LJ_FASTCALL lj_buf_putstr_lower(SBuf *sb, GCstr *s);
LJ_FUNCA SBuf * LJ_FASTCALL lj_buf_putstr_upper(SBuf *sb, GCstr *s);
LJ_FUNC SBuf *lj_buf_putstr_rep(SBuf *sb, GCstr *s, int32_t rep);
LJ_FUNCA GCstr * LJ_FASTCALL lj_buf_tostr(SBuf *sb);
LJ_FUNC GCstr *lj_buf_cat2str(lua_State *L, GCstr *s1, GCstr *s2);
LJ_FUNC uint32_t LJ_FASTCALL lj_buf_ruleb128(const char **pp);

View File

@ -61,7 +61,7 @@ typedef uint16_t HotCount;
#define HOTCOUNT_CALL 1
/* This solves a circular dependency problem -- bump as needed. Sigh. */
#define GG_NUM_ASMFF 58
#define GG_NUM_ASMFF 57
#define GG_LEN_DDISP (BC__MAX + GG_NUM_ASMFF)
#define GG_LEN_SDISP BC_FUNCF

View File

@ -1782,33 +1782,6 @@ static void build_subroutines(BuildCtx *ctx)
| mvn CARG2, #~LJ_TSTR
| b ->fff_restv
|
|.ffunc string_rep // Only handle the 1-char case inline.
| ffgccheck
| ldrd CARG12, [BASE]
| ldrd CARG34, [BASE, #8]
| cmp NARGS8:RC, #16
| bne ->fff_fallback // Exactly 2 arguments
| checktp CARG2, LJ_TSTR
| checktpeq CARG4, LJ_TISNUM
| bne ->fff_fallback
| subs CARG4, CARG3, #1
| ldr CARG2, STR:CARG1->len
| blt ->fff_emptystr // Count <= 0?
| cmp CARG2, #1
| blo ->fff_emptystr // Zero-length string?
| bne ->fff_fallback // Fallback for > 1-char strings.
| ldr CARG2, [DISPATCH, #DISPATCH_GL(tmpbuf.b)]
| ldr RB, [DISPATCH, #DISPATCH_GL(tmpbuf.e)]
| ldr CARG1, STR:CARG1[1]
| add INS, CARG2, CARG3
| cmp RB, INS
| blo ->fff_fallback
|1: // Fill buffer with char.
| strb CARG1, [CARG2, CARG4]
| subs CARG4, CARG4, #1
| bge <1
| b ->fff_newstr
|
|.macro ffstring_op, name
| .ffunc string_ .. name
| ffgccheck

View File

@ -1716,41 +1716,6 @@ static void build_subroutines(BuildCtx *ctx)
| b ->fff_restv
|. li CARG3, LJ_TSTR
|
|.ffunc string_rep // Only handle the 1-char case inline.
| ffgccheck
| lw TMP0, HI(BASE)
| addiu AT, NARGS8:RC, -16 // Exactly 2 arguments.
| lw CARG4, 8+HI(BASE)
| lw STR:CARG1, LO(BASE)
| addiu TMP0, TMP0, -LJ_TSTR
| ldc1 f0, 8(BASE)
| or AT, AT, TMP0
| bnez AT, ->fff_fallback
|. sltiu AT, CARG4, LJ_TISNUM
| trunc.w.d f0, f0
| beqz AT, ->fff_fallback
|. lw TMP0, STR:CARG1->len
| mfc1 CARG3, f0
| lw CARG2, DISPATCH_GL(tmpbuf.b)(DISPATCH)
| lw TMP1, DISPATCH_GL(tmpbuf.e)(DISPATCH)
| li AT, 1
| blez CARG3, ->fff_emptystr // Count <= 0?
|. sltu AT, AT, TMP0
| beqz TMP0, ->fff_emptystr // Zero length string?
|. addu TMP3, CARG2, CARG3
| sltu TMP0, TMP1, TMP3
| or AT, AT, TMP0
| bnez AT, ->fff_fallback // Fallback for > 1-char strings.
|. lbu TMP0, STR:CARG1[1]
| addu TMP2, CARG2, CARG3
|1: // Fill buffer with char. Yes, this is suboptimal code (do you care?).
| addiu TMP2, TMP2, -1
| sltu AT, CARG2, TMP2
| bnez AT, <1
|. sb TMP0, 0(TMP2)
| b ->fff_newstr
|. nop
|
|.macro ffstring_op, name
| .ffunc string_ .. name
| ffgccheck

View File

@ -2177,49 +2177,6 @@ static void build_subroutines(BuildCtx *ctx)
| addi TMP1, TMP1, 1 // start = 1 + (start ? start+len : 0)
| b <3
|
|.ffunc string_rep // Only handle the 1-char case inline.
| ffgccheck
| cmplwi NARGS8:RC, 16
| lwz TMP0, 0(BASE)
| lwz STR:CARG1, 4(BASE)
| lwz CARG4, 8(BASE)
|.if DUALNUM
| lwz CARG3, 12(BASE)
|.else
| lfd FARG2, 8(BASE)
|.endif
| bne ->fff_fallback // Exactly 2 arguments.
| checkstr TMP0; bne ->fff_fallback
|.if DUALNUM
| checknum CARG4; bne ->fff_fallback
|.else
| checknum CARG4; bge ->fff_fallback
| toint CARG3, FARG2
|.endif
| lwz TMP0, STR:CARG1->len
| cmpwi CARG3, 0
| lwz TMP1, DISPATCH_GL(tmpbuf.e)(DISPATCH)
| lwz CARG2, DISPATCH_GL(tmpbuf.b)(DISPATCH)
| ble >2 // Count <= 0? (or non-int)
| cmplwi TMP0, 1
| add TMP3, CARG2, CARG3
| subi TMP2, CARG3, 1
| blt >2 // Zero length string?
| cmplw cr1, TMP1, TMP3
| bne ->fff_fallback // Fallback for > 1-char strings.
| lbz TMP0, STR:CARG1[1]
| blt cr1, ->fff_fallback
|1: // Fill buffer with char. Yes, this is suboptimal code (do you care?).
| cmplwi TMP2, 0
| stbx TMP0, CARG2, TMP2
| subi TMP2, TMP2, 1
| bne <1
| b ->fff_newstr
|2: // Return empty string.
| la STR:CARG1, DISPATCH_GL(strempty)(DISPATCH)
| li CARG3, LJ_TSTR
| b ->fff_restv
|
|.macro ffstring_op, name
| .ffunc string_ .. name
| ffgccheck

View File

@ -2331,41 +2331,6 @@ static void build_subroutines(BuildCtx *ctx)
| xor RC, RC // Zero length. Any ptr in RB is ok.
| jmp <4
|
|.ffunc string_rep // Only handle the 1-char case inline.
| ffgccheck
| cmp NARGS:RD, 2+1; jne ->fff_fallback // Exactly 2 arguments.
| cmp dword [BASE+4], LJ_TSTR; jne ->fff_fallback
| cmp dword [BASE+12], LJ_TISNUM
| mov STR:RB, [BASE]
|.if DUALNUM
| jne ->fff_fallback
| mov RC, dword [BASE+8]
|.else
| jae ->fff_fallback
| cvttsd2si RC, qword [BASE+8]
|.endif
| test RC, RC
| jle ->fff_emptystr // Count <= 0? (or non-int)
| cmp dword STR:RB->len, 1
| jb ->fff_emptystr // Zero length string?
| jne ->fff_fallback_2 // Fallback for > 1-char strings.
| movzx RA, byte STR:RB[1]
| mov RB, [DISPATCH+DISPATCH_GL(tmpbuf.b)]
| add RB, RC
| cmp [DISPATCH+DISPATCH_GL(tmpbuf.e)], RB; jb ->fff_fallback_2
|.if X64
| mov TMP3, RC
|.else
| mov ARG3, RC
|.endif
|1: // Fill buffer with char.
| sub RB, 1
| sub RC, 1
| mov [RB], RAL
| jnz <1
| mov RD, [DISPATCH+DISPATCH_GL(tmpbuf.b)]
| jmp ->fff_newstr
|
|.macro ffstring_op, name
| .ffunc_1 string_ .. name
| ffgccheck