From 9b8db403f28bc8b290f275ea3967d29ebccedde1 Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Fri, 26 Apr 2013 19:57:25 +0200 Subject: [PATCH] Refactor string.rep(). --- src/lib_string.c | 56 +++++++++++------------------------------------ src/lj_buf.c | 24 ++++++++++++++++++++ src/lj_buf.h | 1 + src/lj_dispatch.h | 2 +- src/vm_arm.dasc | 27 ----------------------- src/vm_mips.dasc | 35 ----------------------------- src/vm_ppc.dasc | 43 ------------------------------------ src/vm_x86.dasc | 35 ----------------------------- 8 files changed, 39 insertions(+), 184 deletions(-) diff --git a/src/lib_string.c b/src/lib_string.c index cd673478..598cd93e 100644 --- a/src/lib_string.c +++ b/src/lib_string.c @@ -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) diff --git a/src/lj_buf.c b/src/lj_buf.c index 3526a6e5..55a885a9 100644 --- a/src/lj_buf.c +++ b/src/lj_buf.c @@ -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)); diff --git a/src/lj_buf.h b/src/lj_buf.h index 92a6055a..4d764938 100644 --- a/src/lj_buf.h +++ b/src/lj_buf.h @@ -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); diff --git a/src/lj_dispatch.h b/src/lj_dispatch.h index e744e3f8..1ca29329 100644 --- a/src/lj_dispatch.h +++ b/src/lj_dispatch.h @@ -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 diff --git a/src/vm_arm.dasc b/src/vm_arm.dasc index 5c149bc4..0c542e1e 100644 --- a/src/vm_arm.dasc +++ b/src/vm_arm.dasc @@ -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 diff --git a/src/vm_mips.dasc b/src/vm_mips.dasc index 962e9290..9ab68981 100644 --- a/src/vm_mips.dasc +++ b/src/vm_mips.dasc @@ -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 diff --git a/src/vm_ppc.dasc b/src/vm_ppc.dasc index 19211825..733eeb8f 100644 --- a/src/vm_ppc.dasc +++ b/src/vm_ppc.dasc @@ -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 diff --git a/src/vm_x86.dasc b/src/vm_x86.dasc index 3f2959be..b7c5393f 100644 --- a/src/vm_x86.dasc +++ b/src/vm_x86.dasc @@ -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