mirror of
				https://github.com/bellard/quickjs.git
				synced 2025-05-29 01:49:18 +08:00 
			
		
		
		
	Improve string concatenation hack
- add more cases of in place string concatenation this temporary hack improves the microbench timing by 30% but has little impact on the test262 timings.
This commit is contained in:
		
							parent
							
								
									8e21b96738
								
							
						
					
					
						commit
						c06af876f6
					
				
							
								
								
									
										3
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								Makefile
									
									
									
									
									
								
							| @ -48,9 +48,10 @@ PREFIX?=/usr/local | |||||||
| #CONFIG_ASAN=y
 | #CONFIG_ASAN=y
 | ||||||
| # use memory sanitizer
 | # use memory sanitizer
 | ||||||
| #CONFIG_MSAN=y
 | #CONFIG_MSAN=y
 | ||||||
| # include the code for BigFloat/BigDecimal, math mode and faster large integers
 |  | ||||||
| # use UB sanitizer
 | # use UB sanitizer
 | ||||||
| #CONFIG_UBSAN=y
 | #CONFIG_UBSAN=y
 | ||||||
|  | 
 | ||||||
|  | # include the code for BigFloat/BigDecimal and math mode
 | ||||||
| CONFIG_BIGNUM=y | CONFIG_BIGNUM=y | ||||||
| 
 | 
 | ||||||
| OBJDIR=.obj | OBJDIR=.obj | ||||||
|  | |||||||
							
								
								
									
										168
									
								
								quickjs.c
									
									
									
									
									
								
							
							
						
						
									
										168
									
								
								quickjs.c
									
									
									
									
									
								
							| @ -1454,6 +1454,10 @@ static inline int is_digit(int c) { | |||||||
|     return c >= '0' && c <= '9'; |     return c >= '0' && c <= '9'; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static inline int string_get(const JSString *p, int idx) { | ||||||
|  |     return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| typedef struct JSClassShortDef { | typedef struct JSClassShortDef { | ||||||
|     JSAtom class_name; |     JSAtom class_name; | ||||||
|     JSClassFinalizer *finalizer; |     JSClassFinalizer *finalizer; | ||||||
| @ -2427,10 +2431,7 @@ static inline BOOL is_num_string(uint32_t *pval, const JSString *p) | |||||||
|     len = p->len; |     len = p->len; | ||||||
|     if (len == 0 || len > 10) |     if (len == 0 || len > 10) | ||||||
|         return FALSE; |         return FALSE; | ||||||
|     if (p->is_wide_char) |     c = string_get(p, 0); | ||||||
|         c = p->u.str16[0]; |  | ||||||
|     else |  | ||||||
|         c = p->u.str8[0]; |  | ||||||
|     if (is_num(c)) { |     if (is_num(c)) { | ||||||
|         if (c == '0') { |         if (c == '0') { | ||||||
|             if (len != 1) |             if (len != 1) | ||||||
| @ -2439,10 +2440,7 @@ static inline BOOL is_num_string(uint32_t *pval, const JSString *p) | |||||||
|         } else { |         } else { | ||||||
|             n = c - '0'; |             n = c - '0'; | ||||||
|             for(i = 1; i < len; i++) { |             for(i = 1; i < len; i++) { | ||||||
|                 if (p->is_wide_char) |                 c = string_get(p, i); | ||||||
|                     c = p->u.str16[i]; |  | ||||||
|                 else |  | ||||||
|                     c = p->u.str8[i]; |  | ||||||
|                 if (!is_num(c)) |                 if (!is_num(c)) | ||||||
|                     return FALSE; |                     return FALSE; | ||||||
|                 n64 = (uint64_t)n * 10 + (c - '0'); |                 n64 = (uint64_t)n * 10 + (c - '0'); | ||||||
| @ -2487,23 +2485,8 @@ static uint32_t hash_string(const JSString *str, uint32_t h) | |||||||
|     return h; |     return h; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static __maybe_unused void JS_DumpString(JSRuntime *rt, | static __maybe_unused void JS_DumpChar(JSRuntime *rt, int c, int sep) | ||||||
|                                                   const JSString *p) |  | ||||||
| { | { | ||||||
|     int i, c, sep; |  | ||||||
| 
 |  | ||||||
|     if (p == NULL) { |  | ||||||
|         printf("<null>"); |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
|     printf("%d", p->header.ref_count); |  | ||||||
|     sep = (p->header.ref_count == 1) ? '\"' : '\''; |  | ||||||
|     putchar(sep); |  | ||||||
|     for(i = 0; i < p->len; i++) { |  | ||||||
|         if (p->is_wide_char) |  | ||||||
|             c = p->u.str16[i]; |  | ||||||
|         else |  | ||||||
|             c = p->u.str8[i]; |  | ||||||
|     if (c == sep || c == '\\') { |     if (c == sep || c == '\\') { | ||||||
|         putchar('\\'); |         putchar('\\'); | ||||||
|         putchar(c); |         putchar(c); | ||||||
| @ -2515,6 +2498,21 @@ static __maybe_unused void JS_DumpString(JSRuntime *rt, | |||||||
|     } else { |     } else { | ||||||
|         printf("\\u%04x", c); |         printf("\\u%04x", c); | ||||||
|     } |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p) | ||||||
|  | { | ||||||
|  |     int i, sep; | ||||||
|  | 
 | ||||||
|  |     if (p == NULL) { | ||||||
|  |         printf("<null>"); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     printf("%d", p->header.ref_count); | ||||||
|  |     sep = (p->header.ref_count == 1) ? '\"' : '\''; | ||||||
|  |     putchar(sep); | ||||||
|  |     for(i = 0; i < p->len; i++) { | ||||||
|  |         JS_DumpChar(rt, string_get(p, i), sep); | ||||||
|     } |     } | ||||||
|     putchar(sep); |     putchar(sep); | ||||||
| } | } | ||||||
| @ -2857,6 +2855,7 @@ static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, | |||||||
|     return __JS_NewAtom(rt, p, atom_type); |     return __JS_NewAtom(rt, p, atom_type); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* Warning: str must be ASCII only */ | ||||||
| static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len, | static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len, | ||||||
|                             int atom_type) |                             int atom_type) | ||||||
| { | { | ||||||
| @ -2951,11 +2950,13 @@ static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p) | |||||||
|     return __JS_NewAtom(rt, p, JS_ATOM_TYPE_STRING); |     return __JS_NewAtom(rt, p, JS_ATOM_TYPE_STRING); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* str is UTF-8 encoded */ | ||||||
| JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len) | JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len) | ||||||
| { | { | ||||||
|     JSValue val; |     JSValue val; | ||||||
| 
 | 
 | ||||||
|     if (len == 0 || !is_digit(*str)) { |     if (len == 0 || !is_digit(*str)) { | ||||||
|  |         // XXX: this will not work if UTF-8 encoded str contains non ASCII bytes
 | ||||||
|         JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING); |         JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING); | ||||||
|         if (atom) |         if (atom) | ||||||
|             return atom; |             return atom; | ||||||
| @ -3061,10 +3062,7 @@ static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size, | |||||||
|                         return (const char *)str->u.str8; |                         return (const char *)str->u.str8; | ||||||
|                 } |                 } | ||||||
|                 for(i = 0; i < str->len; i++) { |                 for(i = 0; i < str->len; i++) { | ||||||
|                     if (str->is_wide_char) |                     c = string_get(str, i); | ||||||
|                         c = str->u.str16[i]; |  | ||||||
|                     else |  | ||||||
|                         c = str->u.str8[i]; |  | ||||||
|                     if ((q - buf) >= buf_size - UTF8_CHAR_LEN_MAX) |                     if ((q - buf) >= buf_size - UTF8_CHAR_LEN_MAX) | ||||||
|                         break; |                         break; | ||||||
|                     if (c < 128) { |                     if (c < 128) { | ||||||
| @ -3695,10 +3693,6 @@ static int string_buffer_putc(StringBuffer *s, uint32_t c) | |||||||
|     return string_buffer_putc16(s, c); |     return string_buffer_putc16(s, c); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int string_get(const JSString *p, int idx) { |  | ||||||
|     return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx]; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static int string_getc(const JSString *p, int *pidx) | static int string_getc(const JSString *p, int *pidx) | ||||||
| { | { | ||||||
|     int idx, c, c1; |     int idx, c, c1; | ||||||
| @ -4185,6 +4179,42 @@ static JSValue JS_ConcatString1(JSContext *ctx, | |||||||
|     return JS_MKPTR(JS_TAG_STRING, p); |     return JS_MKPTR(JS_TAG_STRING, p); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static BOOL JS_ConcatStringInPlace(JSContext *ctx, JSString *p1, JSValueConst op2) { | ||||||
|  |     if (JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) { | ||||||
|  |         JSString *p2 = JS_VALUE_GET_STRING(op2); | ||||||
|  |         size_t size1; | ||||||
|  | 
 | ||||||
|  |         if (p2->len == 0) | ||||||
|  |             return TRUE; | ||||||
|  |         if (p1->header.ref_count != 1) | ||||||
|  |             return FALSE; | ||||||
|  |         size1 = js_malloc_usable_size(ctx, p1); | ||||||
|  |         if (p1->is_wide_char) { | ||||||
|  |             if (size1 >= sizeof(*p1) + ((p1->len + p2->len) << 1)) { | ||||||
|  |                 if (p2->is_wide_char) { | ||||||
|  |                     memcpy(p1->u.str16 + p1->len, p2->u.str16, p2->len << 1); | ||||||
|  |                     p1->len += p2->len; | ||||||
|  |                     return TRUE; | ||||||
|  |                 } else { | ||||||
|  |                     size_t i; | ||||||
|  |                     for (i = 0; i < p2->len; i++) { | ||||||
|  |                         p1->u.str16[p1->len++] = p2->u.str8[i]; | ||||||
|  |                     } | ||||||
|  |                     return TRUE; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } else if (!p2->is_wide_char) { | ||||||
|  |             if (size1 >= sizeof(*p1) + p1->len + p2->len + 1) { | ||||||
|  |                 memcpy(p1->u.str8 + p1->len, p2->u.str8, p2->len); | ||||||
|  |                 p1->len += p2->len; | ||||||
|  |                 p1->u.str8[p1->len] = '\0'; | ||||||
|  |                 return TRUE; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return FALSE; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* op1 and op2 are converted to strings. For convenience, op1 or op2 =
 | /* op1 and op2 are converted to strings. For convenience, op1 or op2 =
 | ||||||
|    JS_EXCEPTION are accepted and return JS_EXCEPTION.  */ |    JS_EXCEPTION are accepted and return JS_EXCEPTION.  */ | ||||||
| static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2) | static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2) | ||||||
| @ -4207,27 +4237,11 @@ static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2) | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     p1 = JS_VALUE_GET_STRING(op1); |     p1 = JS_VALUE_GET_STRING(op1); | ||||||
|     p2 = JS_VALUE_GET_STRING(op2); |     if (JS_ConcatStringInPlace(ctx, p1, op2)) { | ||||||
| 
 |  | ||||||
|     /* XXX: could also check if p1 is empty */ |  | ||||||
|     if (p2->len == 0) { |  | ||||||
|         goto ret_op1; |  | ||||||
|     } |  | ||||||
|     if (p1->header.ref_count == 1 && p1->is_wide_char == p2->is_wide_char |  | ||||||
|     &&  js_malloc_usable_size(ctx, p1) >= sizeof(*p1) + ((p1->len + p2->len) << p2->is_wide_char) + 1 - p1->is_wide_char) { |  | ||||||
|         /* Concatenate in place in available space at the end of p1 */ |  | ||||||
|         if (p1->is_wide_char) { |  | ||||||
|             memcpy(p1->u.str16 + p1->len, p2->u.str16, p2->len << 1); |  | ||||||
|             p1->len += p2->len; |  | ||||||
|         } else { |  | ||||||
|             memcpy(p1->u.str8 + p1->len, p2->u.str8, p2->len); |  | ||||||
|             p1->len += p2->len; |  | ||||||
|             p1->u.str8[p1->len] = '\0'; |  | ||||||
|         } |  | ||||||
|     ret_op1: |  | ||||||
|         JS_FreeValue(ctx, op2); |         JS_FreeValue(ctx, op2); | ||||||
|         return op1; |         return op1; | ||||||
|     } |     } | ||||||
|  |     p2 = JS_VALUE_GET_STRING(op2); | ||||||
|     ret = JS_ConcatString1(ctx, p1, p2); |     ret = JS_ConcatString1(ctx, p1, p2); | ||||||
|     JS_FreeValue(ctx, op1); |     JS_FreeValue(ctx, op1); | ||||||
|     JS_FreeValue(ctx, op2); |     JS_FreeValue(ctx, op2); | ||||||
| @ -17778,6 +17792,11 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, | |||||||
|                     sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) + |                     sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) + | ||||||
|                                              JS_VALUE_GET_FLOAT64(op2)); |                                              JS_VALUE_GET_FLOAT64(op2)); | ||||||
|                     sp--; |                     sp--; | ||||||
|  |                 } else if (JS_IsString(op1) && JS_IsString(op2)) { | ||||||
|  |                     sp[-2] = JS_ConcatString(ctx, op1, op2); | ||||||
|  |                     sp--; | ||||||
|  |                     if (JS_IsException(sp[-1])) | ||||||
|  |                         goto exception; | ||||||
|                 } else { |                 } else { | ||||||
|                 add_slow: |                 add_slow: | ||||||
|                     if (js_add_slow(ctx, sp)) |                     if (js_add_slow(ctx, sp)) | ||||||
| @ -17788,38 +17807,45 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, | |||||||
|             BREAK; |             BREAK; | ||||||
|         CASE(OP_add_loc): |         CASE(OP_add_loc): | ||||||
|             { |             { | ||||||
|  |                 JSValue op2; | ||||||
|                 JSValue *pv; |                 JSValue *pv; | ||||||
|                 int idx; |                 int idx; | ||||||
|                 idx = *pc; |                 idx = *pc; | ||||||
|                 pc += 1; |                 pc += 1; | ||||||
| 
 | 
 | ||||||
|  |                 op2 = sp[-1]; | ||||||
|                 pv = &var_buf[idx]; |                 pv = &var_buf[idx]; | ||||||
|                 if (likely(JS_VALUE_IS_BOTH_INT(*pv, sp[-1]))) { |                 if (likely(JS_VALUE_IS_BOTH_INT(*pv, op2))) { | ||||||
|                     int64_t r; |                     int64_t r; | ||||||
|                     r = (int64_t)JS_VALUE_GET_INT(*pv) + |                     r = (int64_t)JS_VALUE_GET_INT(*pv) + JS_VALUE_GET_INT(op2); | ||||||
|                         JS_VALUE_GET_INT(sp[-1]); |  | ||||||
|                     if (unlikely((int)r != r)) |                     if (unlikely((int)r != r)) | ||||||
|                         goto add_loc_slow; |                         goto add_loc_slow; | ||||||
|                     *pv = JS_NewInt32(ctx, r); |                     *pv = JS_NewInt32(ctx, r); | ||||||
|                     sp--; |                     sp--; | ||||||
|                 } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING) { |                 } else if (JS_VALUE_IS_BOTH_FLOAT(*pv, op2)) { | ||||||
|                     JSValue op1; |                     *pv = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(*pv) + | ||||||
|                     op1 = sp[-1]; |                                                JS_VALUE_GET_FLOAT64(op2)); | ||||||
|                     sp--; |                     sp--; | ||||||
|                     op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); |                 } else if (JS_VALUE_GET_TAG(*pv) == JS_TAG_STRING) { | ||||||
|                     if (JS_IsException(op1)) |                     sp--; | ||||||
|  |                     op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); | ||||||
|  |                     if (JS_IsException(op2)) | ||||||
|                         goto exception; |                         goto exception; | ||||||
|                     op1 = JS_ConcatString(ctx, JS_DupValue(ctx, *pv), op1); |                     if (JS_ConcatStringInPlace(ctx, JS_VALUE_GET_STRING(*pv), op2)) { | ||||||
|                     if (JS_IsException(op1)) |                         JS_FreeValue(ctx, op2); | ||||||
|  |                     } else { | ||||||
|  |                         op2 = JS_ConcatString(ctx, JS_DupValue(ctx, *pv), op2); | ||||||
|  |                         if (JS_IsException(op2)) | ||||||
|                             goto exception; |                             goto exception; | ||||||
|                     set_value(ctx, pv, op1); |                         set_value(ctx, pv, op2); | ||||||
|  |                     } | ||||||
|                 } else { |                 } else { | ||||||
|                     JSValue ops[2]; |                     JSValue ops[2]; | ||||||
|                 add_loc_slow: |                 add_loc_slow: | ||||||
|                     /* In case of exception, js_add_slow frees ops[0]
 |                     /* In case of exception, js_add_slow frees ops[0]
 | ||||||
|                        and ops[1], so we must duplicate *pv */ |                        and ops[1], so we must duplicate *pv */ | ||||||
|                     ops[0] = JS_DupValue(ctx, *pv); |                     ops[0] = JS_DupValue(ctx, *pv); | ||||||
|                     ops[1] = sp[-1]; |                     ops[1] = op2; | ||||||
|                     sp--; |                     sp--; | ||||||
|                     if (js_add_slow(ctx, ops + 2)) |                     if (js_add_slow(ctx, ops + 2)) | ||||||
|                         goto exception; |                         goto exception; | ||||||
| @ -35685,6 +35711,7 @@ static JSString *JS_ReadString(BCReaderState *s) | |||||||
|         js_free_string(s->ctx->rt, p); |         js_free_string(s->ctx->rt, p); | ||||||
|         return NULL; |         return NULL; | ||||||
|     } |     } | ||||||
|  |     // XXX: potential endianness issue
 | ||||||
|     memcpy(p->u.str8, s->ptr, size); |     memcpy(p->u.str8, s->ptr, size); | ||||||
|     s->ptr += size; |     s->ptr += size; | ||||||
|     if (!is_wide_char) { |     if (!is_wide_char) { | ||||||
| @ -41142,10 +41169,7 @@ static int js_string_get_own_property(JSContext *ctx, | |||||||
|             idx = __JS_AtomToUInt32(prop); |             idx = __JS_AtomToUInt32(prop); | ||||||
|             if (idx < p1->len) { |             if (idx < p1->len) { | ||||||
|                 if (desc) { |                 if (desc) { | ||||||
|                     if (p1->is_wide_char) |                     ch = string_get(p1, idx); | ||||||
|                         ch = p1->u.str16[idx]; |  | ||||||
|                     else |  | ||||||
|                         ch = p1->u.str8[idx]; |  | ||||||
|                     desc->flags = JS_PROP_ENUMERABLE; |                     desc->flags = JS_PROP_ENUMERABLE; | ||||||
|                     desc->value = js_new_string_char(ctx, ch); |                     desc->value = js_new_string_char(ctx, ch); | ||||||
|                     desc->getter = JS_UNDEFINED; |                     desc->getter = JS_UNDEFINED; | ||||||
| @ -41411,10 +41435,7 @@ static JSValue js_string_charCodeAt(JSContext *ctx, JSValueConst this_val, | |||||||
|     if (idx < 0 || idx >= p->len) { |     if (idx < 0 || idx >= p->len) { | ||||||
|         ret = JS_NAN; |         ret = JS_NAN; | ||||||
|     } else { |     } else { | ||||||
|         if (p->is_wide_char) |         c = string_get(p, idx); | ||||||
|             c = p->u.str16[idx]; |  | ||||||
|         else |  | ||||||
|             c = p->u.str8[idx]; |  | ||||||
|         ret = JS_NewInt32(ctx, c); |         ret = JS_NewInt32(ctx, c); | ||||||
|     } |     } | ||||||
|     JS_FreeValue(ctx, val); |     JS_FreeValue(ctx, val); | ||||||
| @ -41444,10 +41465,7 @@ static JSValue js_string_charAt(JSContext *ctx, JSValueConst this_val, | |||||||
|         else |         else | ||||||
|             ret = js_new_string8(ctx, NULL, 0); |             ret = js_new_string8(ctx, NULL, 0); | ||||||
|     } else { |     } else { | ||||||
|         if (p->is_wide_char) |         c = string_get(p, idx); | ||||||
|             c = p->u.str16[idx]; |  | ||||||
|         else |  | ||||||
|             c = p->u.str8[idx]; |  | ||||||
|         ret = js_new_string_char(ctx, c); |         ret = js_new_string_char(ctx, c); | ||||||
|     } |     } | ||||||
|     JS_FreeValue(ctx, val); |     JS_FreeValue(ctx, val); | ||||||
| @ -45324,7 +45342,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, | |||||||
|                     has_content = TRUE; |                     has_content = TRUE; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             if (has_content && JS_VALUE_GET_STRING(jsc->gap)->len != 0) { |             if (has_content && !JS_IsEmptyString(jsc->gap)) { | ||||||
|                 string_buffer_putc8(jsc->b, '\n'); |                 string_buffer_putc8(jsc->b, '\n'); | ||||||
|                 string_buffer_concat_value(jsc->b, indent); |                 string_buffer_concat_value(jsc->b, indent); | ||||||
|             } |             } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user