diff --git a/Makefile b/Makefile index 8b9555aa..1b245eaf 100644 --- a/Makefile +++ b/Makefile @@ -1033,11 +1033,11 @@ perftest: duk for i in perf-testcases/*.js; do echo \ "`basename $$i`:" \ "duk `time -f %U -o /tmp/time --quiet ./duk $$i >/dev/null 2>&1; cat /tmp/time; rm /tmp/time`" \ - "rhino `time -f %U -o /tmp/time --quiet rhino $$i >/dev/null 2>&1; cat /tmp/time; rm /tmp/time`" \ - "lua `time -f %U -o /tmp/time --quiet lua $${i%%.js}.lua >/dev/null 2>&1; cat /tmp/time; rm /tmp/time`" \ - "python `time -f %U -o /tmp/time --quiet python $${i%%.js}.py >/dev/null 2>&1; cat /tmp/time; rm /tmp/time`" \ - "perl `time -f %U -o /tmp/time --quiet perl $${i%%.js}.pl >/dev/null 2>&1; cat /tmp/time; rm /tmp/time`" \ - "ruby `time -f %U -o /tmp/time --quiet ruby $${i%%.js}.rb >/dev/null 2>&1; cat /tmp/time; rm /tmp/time`"; \ + "rhino `time -f %U -o /tmp/time --quiet rhino $$i >/dev/null 2>&1; if [ $$? -eq 0 ]; then cat /tmp/time; else echo -n n/a; fi; rm /tmp/time`" \ + "lua `time -f %U -o /tmp/time --quiet lua $${i%%.js}.lua >/dev/null 2>&1; if [ $$? -eq 0 ]; then cat /tmp/time; else echo -n n/a; fi; rm /tmp/time`" \ + "python `time -f %U -o /tmp/time --quiet python $${i%%.js}.py >/dev/null 2>&1; if [ $$? -eq 0 ]; then cat /tmp/time; else echo -n n/a; fi; rm /tmp/time`" \ + "perl `time -f %U -o /tmp/time --quiet perl $${i%%.js}.pl >/dev/null 2>&1; if [ $$? -eq 0 ]; then cat /tmp/time; else echo -n n/a; fi; rm /tmp/time`" \ + "ruby `time -f %U -o /tmp/time --quiet ruby $${i%%.js}.rb >/dev/null 2>&1; if [ $$? -eq 0 ]; then cat /tmp/time; else echo -n n/a; fi; rm /tmp/time`"; \ done perftestduk: duk for i in perf-testcases/*.js; do echo \ diff --git a/RELEASES.rst b/RELEASES.rst index dc44a8ea..05a797a8 100644 --- a/RELEASES.rst +++ b/RELEASES.rst @@ -829,6 +829,9 @@ Planned * Accept 32-bit codepoints in String.fromCharCode() to better support non-BMP strings (GH-120) +* Internal performance improvement: direct refcount manipulation from macros + instead of doing a helper function call + * Fix Unicode handling of CJK ideographs and Hangul which were incorrectly not accepted in identifier names (see GH-103) diff --git a/doc/memory-management.rst b/doc/memory-management.rst index 72cf7467..64b0f229 100644 --- a/doc/memory-management.rst +++ b/doc/memory-management.rst @@ -730,14 +730,14 @@ Macros: * ``DUK_HEAPHDR_DECREF`` -* and a bunch of heap element type specific INCREF/DECREF macros, - defined in ``heaphdr.h`` +* and a bunch of heap element type specific INCREF/DECREF macros and + helpers, defined in ``heaphdr.h`` Notes on macro semantics: -* The macros tolerate ``NULL`` pointers, which are simply ignored. This - reduces caller code size but requires a pointer check which is unnecessary - in the vast majority of cases. +* The macros are optimized for performance and don't tolerate a ``NULL`` + pointer by default. There are ``_ALLOWNULL`` variants for cases where + NULLs may actually occur. * An ``INCREF`` is guaranteed not to have any side effects. diff --git a/src/duk_api_heap.c b/src/duk_api_heap.c index b688d6c7..8e5936a6 100644 --- a/src/duk_api_heap.c +++ b/src/duk_api_heap.c @@ -84,7 +84,7 @@ DUK_EXTERNAL void duk_set_global_object(duk_context *ctx) { h_prev_glob = thr->builtins[DUK_BIDX_GLOBAL]; thr->builtins[DUK_BIDX_GLOBAL] = h_glob; DUK_HOBJECT_INCREF(thr, h_glob); - DUK_HOBJECT_DECREF(thr, h_prev_glob); /* side effects, in theory (referenced by global env) */ + DUK_HOBJECT_DECREF_ALLOWNULL(thr, h_prev_glob); /* side effects, in theory (referenced by global env) */ /* * Replace lexical environment for global scope @@ -116,7 +116,7 @@ DUK_EXTERNAL void duk_set_global_object(duk_context *ctx) { h_prev_env = thr->builtins[DUK_BIDX_GLOBAL_ENV]; thr->builtins[DUK_BIDX_GLOBAL_ENV] = h_env; DUK_HOBJECT_INCREF(thr, h_env); - DUK_HOBJECT_DECREF(thr, h_prev_env); /* side effects */ + DUK_HOBJECT_DECREF_ALLOWNULL(thr, h_prev_env); /* side effects */ DUK_UNREF(h_env); /* without refcounts */ DUK_UNREF(h_prev_env); diff --git a/src/duk_error_macros.c b/src/duk_error_macros.c index cd5de0ea..025b2b0b 100644 --- a/src/duk_error_macros.c +++ b/src/duk_error_macros.c @@ -55,10 +55,14 @@ DUK_INTERNAL void duk_err_handle_error(duk_hthread *thr, duk_errcode_t code) { #else /* DUK_USE_VARIADIC_MACROS */ DUK_INTERNAL void duk_err_handle_error_nonverbose1(duk_hthread *thr, duk_errcode_t code, const char *fmt, ...) { + DUK_UNREF(fmt); duk_err_create_and_throw(thr, code); } DUK_INTERNAL void duk_err_handle_error_nonverbose2(const char *filename, duk_int_t line, duk_hthread *thr, duk_errcode_t code, const char *fmt, ...) { + DUK_UNREF(filename); + DUK_UNREF(line); + DUK_UNREF(fmt); duk_err_create_and_throw(thr, code); } #endif /* DUK_USE_VARIADIC_MACROS */ diff --git a/src/duk_features.h.in b/src/duk_features.h.in index aa51dfc6..7cc4ef82 100644 --- a/src/duk_features.h.in +++ b/src/duk_features.h.in @@ -2233,13 +2233,18 @@ typedef FILE duk_file; #endif /* - * General prefer speed/size flag - * - * Catch-all flag which can be used to choose between variant algorithms - * where a speed-size tradeoff exists (e.g. lookup tables). When it really - * matters, specific use flags may be appropriate. + * Speed/size and other performance options + */ + +/* Use fast ("inline") refcount operations instead of calling out to helpers + * by default. The difference in binary size is small (~1kB on x64). */ +#define DUK_USE_FAST_REFCOUNT_DEFAULT +/* Catch-all flag which can be used to choose between variant algorithms + * where a speed-size tradeoff exists (e.g. lookup tables). When it really + * matters, specific use flags may be appropriate. + */ #define DUK_USE_PREFER_SIZE /* diff --git a/src/duk_heap.h b/src/duk_heap.h index 4bace273..c2514ec2 100644 --- a/src/duk_heap.h +++ b/src/duk_heap.h @@ -572,11 +572,26 @@ DUK_INTERNAL_DECL void *duk_heap_mem_realloc_indirect(duk_heap *heap, duk_mem_ge DUK_INTERNAL_DECL void duk_heap_mem_free(duk_heap *heap, void *ptr); #ifdef DUK_USE_REFERENCE_COUNTING -DUK_INTERNAL_DECL void duk_heap_tval_incref(duk_tval *tv); -DUK_INTERNAL_DECL void duk_heap_tval_decref(duk_hthread *thr, duk_tval *tv); -DUK_INTERNAL_DECL void duk_heap_heaphdr_incref(duk_heaphdr *h); -DUK_INTERNAL_DECL void duk_heap_heaphdr_decref(duk_hthread *thr, duk_heaphdr *h); -DUK_INTERNAL_DECL void duk_heap_refcount_finalize_heaphdr(duk_hthread *thr, duk_heaphdr *hdr); +#if !defined(DUK_USE_FAST_REFCOUNT_DEFAULT) +DUK_INTERNAL_DECL void duk_tval_incref(duk_tval *tv); +#endif +#if 0 /* unused */ +DUK_INTERNAL_DECL void duk_tval_incref_allownull(duk_tval *tv); +#endif +DUK_INTERNAL_DECL void duk_tval_decref(duk_hthread *thr, duk_tval *tv); +#if 0 /* unused */ +DUK_INTERNAL_DECL void duk_tval_decref_allownull(duk_hthread *thr, duk_tval *tv); +#endif +#if !defined(DUK_USE_FAST_REFCOUNT_DEFAULT) +DUK_INTERNAL_DECL void duk_heaphdr_incref(duk_heaphdr *h); +#endif +#if 0 /* unused */ +DUK_INTERNAL_DECL void duk_heaphdr_incref_allownull(duk_heaphdr *h); +#endif +DUK_INTERNAL_DECL void duk_heaphdr_decref(duk_hthread *thr, duk_heaphdr *h); +DUK_INTERNAL_DECL void duk_heaphdr_decref_allownull(duk_hthread *thr, duk_heaphdr *h); +DUK_INTERNAL_DECL void duk_heaphdr_refzero(duk_hthread *thr, duk_heaphdr *h); +DUK_INTERNAL_DECL void duk_heaphdr_refcount_finalize(duk_hthread *thr, duk_heaphdr *hdr); #else /* no refcounting */ #endif diff --git a/src/duk_heap_markandsweep.c b/src/duk_heap_markandsweep.c index d8fb98d4..7b591dcb 100644 --- a/src/duk_heap_markandsweep.c +++ b/src/duk_heap_markandsweep.c @@ -453,7 +453,7 @@ DUK_LOCAL void duk__finalize_refcounts(duk_heap *heap) { */ DUK_DDD(DUK_DDDPRINT("unreachable object, refcount finalize before sweeping: %p", (void *) hdr)); - duk_heap_refcount_finalize_heaphdr(thr, hdr); + duk_heaphdr_refcount_finalize(thr, hdr); } hdr = DUK_HEAPHDR_GET_NEXT(heap, hdr); diff --git a/src/duk_heap_refcount.c b/src/duk_heap_refcount.c index 9acec809..e686a92a 100644 --- a/src/duk_heap_refcount.c +++ b/src/duk_heap_refcount.c @@ -64,22 +64,22 @@ DUK_LOCAL void duk__refcount_finalize_hobject(duk_hthread *thr, duk_hobject *h) if (!key) { continue; } - duk_heap_heaphdr_decref(thr, (duk_heaphdr *) key); + duk_heaphdr_decref(thr, (duk_heaphdr *) key); if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, h, i)) { - duk_heap_heaphdr_decref(thr, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, h, i)); - duk_heap_heaphdr_decref(thr, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, h, i)); + duk_heaphdr_decref_allownull(thr, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, h, i)); + duk_heaphdr_decref_allownull(thr, (duk_heaphdr *) DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, h, i)); } else { - duk_heap_tval_decref(thr, DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, h, i)); + duk_tval_decref(thr, DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, h, i)); } } for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(h); i++) { - duk_heap_tval_decref(thr, DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, h, i)); + duk_tval_decref(thr, DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, h, i)); } /* hash part is a 'weak reference' and does not contribute */ - duk_heap_heaphdr_decref(thr, (duk_heaphdr *) DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h)); + duk_heaphdr_decref_allownull(thr, (duk_heaphdr *) DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h)); if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h)) { duk_hcompiledfunction *f = (duk_hcompiledfunction *) h; @@ -91,18 +91,18 @@ DUK_LOCAL void duk__refcount_finalize_hobject(duk_hthread *thr, duk_hobject *h) tv = DUK_HCOMPILEDFUNCTION_GET_CONSTS_BASE(thr->heap, f); tv_end = DUK_HCOMPILEDFUNCTION_GET_CONSTS_END(thr->heap, f); while (tv < tv_end) { - duk_heap_tval_decref(thr, tv); + duk_tval_decref(thr, tv); tv++; } funcs = DUK_HCOMPILEDFUNCTION_GET_FUNCS_BASE(thr->heap, f); funcs_end = DUK_HCOMPILEDFUNCTION_GET_FUNCS_END(thr->heap, f); while (funcs < funcs_end) { - duk_heap_heaphdr_decref(thr, (duk_heaphdr *) *funcs); + duk_heaphdr_decref(thr, (duk_heaphdr *) *funcs); funcs++; } - duk_heap_heaphdr_decref(thr, (duk_heaphdr *) DUK_HCOMPILEDFUNCTION_GET_DATA(thr->heap, f)); + duk_heaphdr_decref(thr, (duk_heaphdr *) DUK_HCOMPILEDFUNCTION_GET_DATA(thr->heap, f)); } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h)) { duk_hnativefunction *f = (duk_hnativefunction *) h; DUK_UNREF(f); @@ -113,17 +113,17 @@ DUK_LOCAL void duk__refcount_finalize_hobject(duk_hthread *thr, duk_hobject *h) tv = t->valstack; while (tv < t->valstack_end) { - duk_heap_tval_decref(thr, tv); + duk_tval_decref(thr, tv); tv++; } for (i = 0; i < (duk_uint_fast32_t) t->callstack_top; i++) { duk_activation *act = t->callstack + i; - duk_heap_heaphdr_decref(thr, (duk_heaphdr *) DUK_ACT_GET_FUNC(act)); - duk_heap_heaphdr_decref(thr, (duk_heaphdr *) act->var_env); - duk_heap_heaphdr_decref(thr, (duk_heaphdr *) act->lex_env); + duk_heaphdr_decref_allownull(thr, (duk_heaphdr *) DUK_ACT_GET_FUNC(act)); + duk_heaphdr_decref_allownull(thr, (duk_heaphdr *) act->var_env); + duk_heaphdr_decref_allownull(thr, (duk_heaphdr *) act->lex_env); #ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY - duk_heap_heaphdr_decref(thr, (duk_heaphdr *) act->prev_caller); + duk_heaphdr_decref_allownull(thr, (duk_heaphdr *) act->prev_caller); #endif } @@ -134,14 +134,14 @@ DUK_LOCAL void duk__refcount_finalize_hobject(duk_hthread *thr, duk_hobject *h) #endif for (i = 0; i < DUK_NUM_BUILTINS; i++) { - duk_heap_heaphdr_decref(thr, (duk_heaphdr *) t->builtins[i]); + duk_heaphdr_decref_allownull(thr, (duk_heaphdr *) t->builtins[i]); } - duk_heap_heaphdr_decref(thr, (duk_heaphdr *) t->resumer); + duk_heaphdr_decref_allownull(thr, (duk_heaphdr *) t->resumer); } } -DUK_INTERNAL void duk_heap_refcount_finalize_heaphdr(duk_hthread *thr, duk_heaphdr *hdr) { +DUK_INTERNAL void duk_heaphdr_refcount_finalize(duk_hthread *thr, duk_heaphdr *hdr) { DUK_ASSERT(hdr); switch ((int) DUK_HEAPHDR_GET_TYPE(hdr)) { @@ -318,88 +318,11 @@ DUK_LOCAL void duk__refzero_free_pending(duk_hthread *thr) { * */ -DUK_INTERNAL void duk_heap_tval_incref(duk_tval *tv) { -#if 0 - DUK_DDD(DUK_DDDPRINT("tval incref %p (%ld->%ld): %!T", - (void *) tv, - (tv != NULL && DUK_TVAL_IS_HEAP_ALLOCATED(tv) ? (long) DUK_TVAL_GET_HEAPHDR(tv)->h_refcount : (long) 0), - (tv != NULL && DUK_TVAL_IS_HEAP_ALLOCATED(tv) ? (long) (DUK_TVAL_GET_HEAPHDR(tv)->h_refcount + 1) : (long) 0), - (duk_tval *) tv)); -#endif - - if (!tv) { - return; - } - - if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { - duk_heaphdr *h = DUK_TVAL_GET_HEAPHDR(tv); - if (h) { - DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h)); - DUK_ASSERT_DISABLE(h->h_refcount >= 0); - DUK_HEAPHDR_PREINC_REFCOUNT(h); - } - } -} - -DUK_INTERNAL void duk_heap_tval_decref(duk_hthread *thr, duk_tval *tv) { -#if 0 - DUK_DDD(DUK_DDDPRINT("tval decref %p (%ld->%ld): %!T", - (void *) tv, - (tv != NULL && DUK_TVAL_IS_HEAP_ALLOCATED(tv) ? (long) DUK_TVAL_GET_HEAPHDR(tv)->h_refcount : (long) 0), - (tv != NULL && DUK_TVAL_IS_HEAP_ALLOCATED(tv) ? (long) (DUK_TVAL_GET_HEAPHDR(tv)->h_refcount - 1) : (long) 0), - (duk_tval *) tv)); -#endif - - if (!tv) { - return; - } - - if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { - duk_heap_heaphdr_decref(thr, DUK_TVAL_GET_HEAPHDR(tv)); - } -} - -DUK_INTERNAL void duk_heap_heaphdr_incref(duk_heaphdr *h) { -#if 0 - DUK_DDD(DUK_DDDPRINT("heaphdr incref %p (%ld->%ld): %!O", - (void *) h, - (h != NULL ? (long) h->h_refcount : (long) 0), - (h != NULL ? (long) (h->h_refcount + 1) : (long) 0), - (duk_heaphdr *) h)); -#endif - - if (!h) { - return; - } - DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h)); - DUK_ASSERT_DISABLE(DUK_HEAPHDR_GET_REFCOUNT(h) >= 0); - - DUK_HEAPHDR_PREINC_REFCOUNT(h); -} - -DUK_INTERNAL void duk_heap_heaphdr_decref(duk_hthread *thr, duk_heaphdr *h) { +DUK_INTERNAL void duk_heaphdr_refzero(duk_hthread *thr, duk_heaphdr *h) { duk_heap *heap; -#if 0 - DUK_DDD(DUK_DDDPRINT("heaphdr decref %p (%ld->%ld): %!O", - (void *) h, - (h != NULL ? (long) h->h_refcount : (long) 0), - (h != NULL ? (long) (h->h_refcount - 1) : (long) 0), - (duk_heaphdr *) h)); -#endif - DUK_ASSERT(thr != NULL); - DUK_ASSERT(thr->heap != NULL); - - if (!h) { - return; - } - DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h)); - DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(h) >= 1); - - if (DUK_HEAPHDR_PREDEC_REFCOUNT(h) != 0) { - return; - } + DUK_ASSERT(h != NULL); heap = thr->heap; DUK_DDD(DUK_DDDPRINT("refzero %p: %!O", (void *) h, (duk_heaphdr *) h)); @@ -463,6 +386,115 @@ DUK_INTERNAL void duk_heap_heaphdr_decref(duk_hthread *thr, duk_heaphdr *h) { } } +#if !defined(DUK_USE_FAST_REFCOUNT_DEFAULT) +DUK_INTERNAL void duk_tval_incref(duk_tval *tv) { + DUK_ASSERT(tv != NULL); + + if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { + duk_heaphdr *h = DUK_TVAL_GET_HEAPHDR(tv); + DUK_ASSERT(h != NULL); + DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h)); + DUK_ASSERT_DISABLE(h->h_refcount >= 0); + DUK_HEAPHDR_PREINC_REFCOUNT(h); + } +} +#endif + +#if 0 /* unused */ +DUK_INTERNAL void duk_tval_incref_allownull(duk_tval *tv) { + if (tv == NULL) { + return; + } + if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { + duk_heaphdr *h = DUK_TVAL_GET_HEAPHDR(tv); + DUK_ASSERT(h != NULL); + DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h)); + DUK_ASSERT_DISABLE(h->h_refcount >= 0); + DUK_HEAPHDR_PREINC_REFCOUNT(h); + } +} +#endif + +DUK_INTERNAL void duk_tval_decref(duk_hthread *thr, duk_tval *tv) { + DUK_ASSERT(thr != NULL); + DUK_ASSERT(tv != NULL); + + if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { + duk_heaphdr *h = DUK_TVAL_GET_HEAPHDR(tv); + DUK_ASSERT(h != NULL); + DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h)); + duk_heaphdr_decref(thr, h); + } +} + +#if 0 /* unused */ +DUK_INTERNAL void duk_tval_decref_allownull(duk_hthread *thr, duk_tval *tv) { + DUK_ASSERT(thr != NULL); + + if (tv == NULL) { + return; + } + if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) { + duk_heaphdr *h = DUK_TVAL_GET_HEAPHDR(tv); + DUK_ASSERT(h != NULL); + DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h)); + duk_heaphdr_decref(thr, h); + } +} +#endif + +#if !defined(DUK_USE_FAST_REFCOUNT_DEFAULT) +DUK_INTERNAL void duk_heaphdr_incref(duk_heaphdr *h) { + DUK_ASSERT(h != NULL); + DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h)); + DUK_ASSERT_DISABLE(DUK_HEAPHDR_GET_REFCOUNT(h) >= 0); + + DUK_HEAPHDR_PREINC_REFCOUNT(h); +} +#endif + +#if 0 /* unused */ +DUK_INTERNAL void duk_heaphdr_incref_allownull(duk_heaphdr *h) { + if (h == NULL) { + return; + } + DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h)); + DUK_ASSERT_DISABLE(DUK_HEAPHDR_GET_REFCOUNT(h) >= 0); + + DUK_HEAPHDR_PREINC_REFCOUNT(h); +} +#endif + +DUK_INTERNAL void duk_heaphdr_decref(duk_hthread *thr, duk_heaphdr *h) { + DUK_ASSERT(thr != NULL); + DUK_ASSERT(thr->heap != NULL); + DUK_ASSERT(h != NULL); + DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h)); + DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(h) >= 1); + + if (DUK_HEAPHDR_PREDEC_REFCOUNT(h) != 0) { + return; + } + duk_heaphdr_refzero(thr, h); +} + +DUK_INTERNAL void duk_heaphdr_decref_allownull(duk_hthread *thr, duk_heaphdr *h) { + DUK_ASSERT(thr != NULL); + DUK_ASSERT(thr->heap != NULL); + + if (h == NULL) { + return; + } + + DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h)); + DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(h) >= 1); + + if (DUK_HEAPHDR_PREDEC_REFCOUNT(h) != 0) { + return; + } + duk_heaphdr_refzero(thr, h); +} + #else /* no refcounting */ diff --git a/src/duk_heaphdr.h b/src/duk_heaphdr.h index 859b6cac..c9ebe559 100644 --- a/src/duk_heaphdr.h +++ b/src/duk_heaphdr.h @@ -244,43 +244,138 @@ struct duk_heaphdr_string { #if defined(DUK_USE_REFERENCE_COUNTING) -#define DUK_TVAL_INCREF(thr,tv) duk_heap_tval_incref((tv)) -#define DUK_TVAL_DECREF(thr,tv) duk_heap_tval_decref((thr),(tv)) -#define DUK__HEAPHDR_INCREF(thr,h) duk_heap_heaphdr_incref((h)) -#define DUK__HEAPHDR_DECREF(thr,h) duk_heap_heaphdr_decref((thr),(h)) -#define DUK_HEAPHDR_INCREF(thr,h) DUK__HEAPHDR_INCREF((thr),(duk_heaphdr *) (h)) -#define DUK_HEAPHDR_DECREF(thr,h) DUK__HEAPHDR_DECREF((thr),(duk_heaphdr *) (h)) -#define DUK_HSTRING_INCREF(thr,h) DUK__HEAPHDR_INCREF((thr),(duk_heaphdr *) (h)) -#define DUK_HSTRING_DECREF(thr,h) DUK__HEAPHDR_DECREF((thr),(duk_heaphdr *) (h)) -#define DUK_HOBJECT_INCREF(thr,h) DUK__HEAPHDR_INCREF((thr),(duk_heaphdr *) (h)) -#define DUK_HOBJECT_DECREF(thr,h) DUK__HEAPHDR_DECREF((thr),(duk_heaphdr *) (h)) -#define DUK_HBUFFER_INCREF(thr,h) DUK__HEAPHDR_INCREF((thr),(duk_heaphdr *) (h)) -#define DUK_HBUFFER_DECREF(thr,h) DUK__HEAPHDR_DECREF((thr),(duk_heaphdr *) (h)) -#define DUK_HCOMPILEDFUNCTION_INCREF(thr,h) DUK__HEAPHDR_INCREF((thr),(duk_heaphdr *) &(h)->obj) -#define DUK_HCOMPILEDFUNCTION_DECREF(thr,h) DUK__HEAPHDR_DECREF((thr),(duk_heaphdr *) &(h)->obj) -#define DUK_HNATIVEFUNCTION_INCREF(thr,h) DUK__HEAPHDR_INCREF((thr),(duk_heaphdr *) &(h)->obj) -#define DUK_HNATIVEFUNCTION_DECREF(thr,h) DUK__HEAPHDR_DECREF((thr),(duk_heaphdr *) &(h)->obj) -#define DUK_HTHREAD_INCREF(thr,h) DUK__HEAPHDR_INCREF((thr),(duk_heaphdr *) &(h)->obj) -#define DUK_HTHREAD_DECREF(thr,h) DUK__HEAPHDR_DECREF((thr),(duk_heaphdr *) &(h)->obj) +/* Fast variants, inline refcount operations except for refzero handling. + * Can be used explicitly when speed is always more important than size. + * For a good compiler and a single file build, these are basically the + * same as a forced inline. + */ +#define DUK_TVAL_INCREF_FAST(thr,tv) do { \ + duk_tval *duk__tv = (tv); \ + DUK_ASSERT(duk__tv != NULL); \ + if (DUK_TVAL_IS_HEAP_ALLOCATED(duk__tv)) { \ + duk_heaphdr *duk__h = DUK_TVAL_GET_HEAPHDR(duk__tv); \ + DUK_ASSERT(duk__h != NULL); \ + DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(duk__h)); \ + DUK_HEAPHDR_PREINC_REFCOUNT(duk__h); \ + } \ + } while (0) +#define DUK_TVAL_DECREF_FAST(thr,tv) do { \ + duk_tval *duk__tv = (tv); \ + DUK_ASSERT(duk__tv != NULL); \ + if (DUK_TVAL_IS_HEAP_ALLOCATED(duk__tv)) { \ + duk_heaphdr *duk__h = DUK_TVAL_GET_HEAPHDR(duk__tv); \ + DUK_ASSERT(duk__h != NULL); \ + DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(duk__h)); \ + DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(duk__h) > 0); \ + if (DUK_HEAPHDR_PREDEC_REFCOUNT(duk__h) == 0) { \ + duk_heaphdr_refzero(thr, duk__h); \ + } \ + } \ + } while (0) +#define DUK_HEAPHDR_INCREF_FAST(thr,h) do { \ + duk_heaphdr *duk__h = (duk_heaphdr *) (h); \ + DUK_ASSERT(duk__h != NULL); \ + DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(duk__h)); \ + DUK_HEAPHDR_PREINC_REFCOUNT(duk__h); \ + } while (0) +#define DUK_HEAPHDR_DECREF_FAST(thr,h) do { \ + duk_heaphdr *duk__h = (duk_heaphdr *) (h); \ + DUK_ASSERT(duk__h != NULL); \ + DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(duk__h)); \ + DUK_ASSERT(DUK_HEAPHDR_GET_REFCOUNT(duk__h) > 0); \ + if (DUK_HEAPHDR_PREDEC_REFCOUNT(duk__h) == 0) { \ + duk_heaphdr_refzero(thr, duk__h); \ + } \ + } while (0) + +/* Slow variants, call to a helper to reduce code size. + * Can be used explicitly when size is always more important than speed. + */ +#define DUK_TVAL_INCREF_SLOW(thr,tv) do { \ + duk_tval_incref((tv)); \ + } while (0) +#define DUK_TVAL_DECREF_SLOW(thr,tv) do { \ + duk_tval_decref((thr), (tv)); \ + } while (0) +#define DUK_HEAPHDR_INCREF_SLOW(thr,h) do { \ + duk_heaphdr_incref((duk_heaphdr *) (h)); \ + } while (0) +#define DUK_HEAPHDR_DECREF_SLOW(thr,h) do { \ + duk_heaphdr_decref((thr), (duk_heaphdr *) (h)); \ + } while (0) + +/* Default variants. Selection depends on speed/size preference. + * Concretely: with gcc 4.8.1 -Os x64 the difference in final binary + * is about +1kB for _FAST variants. + */ +#if defined(DUK_USE_FAST_REFCOUNT_DEFAULT) +#define DUK_TVAL_INCREF(thr,tv) DUK_TVAL_INCREF_FAST((thr),(tv)) +#define DUK_TVAL_DECREF(thr,tv) DUK_TVAL_DECREF_FAST((thr),(tv)) +#define DUK_HEAPHDR_INCREF(thr,h) DUK_HEAPHDR_INCREF_FAST((thr),(h)) +#define DUK_HEAPHDR_DECREF(thr,h) DUK_HEAPHDR_DECREF_FAST((thr),(h)) +#else +#define DUK_TVAL_INCREF(thr,tv) DUK_TVAL_INCREF_SLOW((thr),(tv)) +#define DUK_TVAL_DECREF(thr,tv) DUK_TVAL_DECREF_SLOW((thr),(tv)) +#define DUK_HEAPHDR_INCREF(thr,h) DUK_HEAPHDR_INCREF_SLOW((thr),(h)) +#define DUK_HEAPHDR_DECREF(thr,h) DUK_HEAPHDR_DECREF_SLOW((thr),(h)) +#endif + +/* Casting convenience. */ +#define DUK_HSTRING_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) (h)) +#define DUK_HSTRING_DECREF(thr,h) DUK_HEAPHDR_DECREF((thr),(duk_heaphdr *) (h)) +#define DUK_HOBJECT_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) (h)) +#define DUK_HOBJECT_DECREF(thr,h) DUK_HEAPHDR_DECREF((thr),(duk_heaphdr *) (h)) +#define DUK_HBUFFER_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) (h)) +#define DUK_HBUFFER_DECREF(thr,h) DUK_HEAPHDR_DECREF((thr),(duk_heaphdr *) (h)) +#define DUK_HCOMPILEDFUNCTION_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) &(h)->obj) +#define DUK_HCOMPILEDFUNCTION_DECREF(thr,h) DUK_HEAPHDR_DECREF((thr),(duk_heaphdr *) &(h)->obj) +#define DUK_HNATIVEFUNCTION_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) &(h)->obj) +#define DUK_HNATIVEFUNCTION_DECREF(thr,h) DUK_HEAPHDR_DECREF((thr),(duk_heaphdr *) &(h)->obj) +#define DUK_HTHREAD_INCREF(thr,h) DUK_HEAPHDR_INCREF((thr),(duk_heaphdr *) &(h)->obj) +#define DUK_HTHREAD_DECREF(thr,h) DUK_HEAPHDR_DECREF((thr),(duk_heaphdr *) &(h)->obj) + +/* Convenience for some situations; the above macros don't allow NULLs + * for performance reasons. + */ +#define DUK_HOBJECT_INCREF_ALLOWNULL(thr,h) do { \ + if ((h) != NULL) { \ + DUK_HEAPHDR_INCREF((thr), (duk_heaphdr *) (h)); \ + } \ + } while (0) +#define DUK_HOBJECT_DECREF_ALLOWNULL(thr,h) do { \ + if ((h) != NULL) { \ + DUK_HEAPHDR_DECREF((thr), (duk_heaphdr *) (h)); \ + } \ + } while (0) #else /* DUK_USE_REFERENCE_COUNTING */ -#define DUK_TVAL_INCREF(thr,v) /* nop */ -#define DUK_TVAL_DECREF(thr,v) /* nop */ -#define DUK_HEAPHDR_INCREF(thr,h) /* nop */ -#define DUK_HEAPHDR_DECREF(thr,h) /* nop */ -#define DUK_HSTRING_INCREF(thr,h) /* nop */ -#define DUK_HSTRING_DECREF(thr,h) /* nop */ -#define DUK_HOBJECT_INCREF(thr,h) /* nop */ -#define DUK_HOBJECT_DECREF(thr,h) /* nop */ -#define DUK_HBUFFER_INCREF(thr,h) /* nop */ -#define DUK_HBUFFER_DECREF(thr,h) /* nop */ -#define DUK_HCOMPILEDFUNCTION_INCREF(thr,h) /* nop */ -#define DUK_HCOMPILEDFUNCTION_DECREF(thr,h) /* nop */ -#define DUK_HNATIVEFUNCTION_INCREF(thr,h) /* nop */ -#define DUK_HNATIVEFUNCTION_DECREF(thr,h) /* nop */ -#define DUK_HTHREAD_INCREF(thr,h) /* nop */ -#define DUK_HTHREAD_DECREF(thr,h) /* nop */ +#define DUK_TVAL_INCREF_FAST(thr,v) do {} while (0) /* nop */ +#define DUK_TVAL_DECREF_FAST(thr,v) do {} while (0) /* nop */ +#define DUK_TVAL_INCREF_SLOW(thr,v) do {} while (0) /* nop */ +#define DUK_TVAL_DECREF_SLOW(thr,v) do {} while (0) /* nop */ +#define DUK_TVAL_INCREF(thr,v) do {} while (0) /* nop */ +#define DUK_TVAL_DECREF(thr,v) do {} while (0) /* nop */ +#define DUK_HEAPHDR_INCREF_FAST(thr,h) do {} while (0) /* nop */ +#define DUK_HEAPHDR_DECREF_FAST(thr,h) do {} while (0) /* nop */ +#define DUK_HEAPHDR_INCREF_SLOW(thr,h) do {} while (0) /* nop */ +#define DUK_HEAPHDR_DECREF_SLOW(thr,h) do {} while (0) /* nop */ +#define DUK_HEAPHDR_INCREF(thr,h) do {} while (0) /* nop */ +#define DUK_HEAPHDR_DECREF(thr,h) do {} while (0) /* nop */ +#define DUK_HSTRING_INCREF(thr,h) do {} while (0) /* nop */ +#define DUK_HSTRING_DECREF(thr,h) do {} while (0) /* nop */ +#define DUK_HOBJECT_INCREF(thr,h) do {} while (0) /* nop */ +#define DUK_HOBJECT_DECREF(thr,h) do {} while (0) /* nop */ +#define DUK_HBUFFER_INCREF(thr,h) do {} while (0) /* nop */ +#define DUK_HBUFFER_DECREF(thr,h) do {} while (0) /* nop */ +#define DUK_HCOMPILEDFUNCTION_INCREF(thr,h) do {} while (0) /* nop */ +#define DUK_HCOMPILEDFUNCTION_DECREF(thr,h) do {} while (0) /* nop */ +#define DUK_HNATIVEFUNCTION_INCREF(thr,h) do {} while (0) /* nop */ +#define DUK_HNATIVEFUNCTION_DECREF(thr,h) do {} while (0) /* nop */ +#define DUK_HTHREAD_INCREF(thr,h) do {} while (0) /* nop */ +#define DUK_HTHREAD_DECREF(thr,h) do {} while (0) /* nop */ +#define DUK_HOBJECT_INCREF_ALLOWNULL(thr,h) do {} while (0) /* nop */ +#define DUK_HOBJECT_DECREF_ALLOWNULL(thr,h) do {} while (0) /* nop */ #endif /* DUK_USE_REFERENCE_COUNTING */ diff --git a/src/duk_hobject_misc.c b/src/duk_hobject_misc.c index b53d9d1a..4ea78d41 100644 --- a/src/duk_hobject_misc.c +++ b/src/duk_hobject_misc.c @@ -37,8 +37,8 @@ DUK_INTERNAL void duk_hobject_set_prototype(duk_hthread *thr, duk_hobject *h, du DUK_ASSERT(h); tmp = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h); DUK_HOBJECT_SET_PROTOTYPE(thr->heap, h, p); - DUK_HOBJECT_INCREF(thr, p); /* avoid problems if p == h->prototype */ - DUK_HOBJECT_DECREF(thr, tmp); + DUK_HOBJECT_INCREF_ALLOWNULL(thr, p); /* avoid problems if p == h->prototype */ + DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp); #else DUK_ASSERT(h); DUK_UNREF(thr); diff --git a/src/duk_hobject_props.c b/src/duk_hobject_props.c index 33b06a47..afd681c0 100644 --- a/src/duk_hobject_props.c +++ b/src/duk_hobject_props.c @@ -3828,12 +3828,12 @@ DUK_INTERNAL duk_bool_t duk_hobject_delprop_raw(duk_hthread *thr, duk_hobject *o tmp = DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, obj, desc.e_idx); DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, obj, desc.e_idx, NULL); DUK_UNREF(tmp); - DUK_HOBJECT_DECREF(thr, tmp); + DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp); tmp = DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, obj, desc.e_idx); DUK_HOBJECT_E_SET_VALUE_SETTER(thr->heap, obj, desc.e_idx, NULL); DUK_UNREF(tmp); - DUK_HOBJECT_DECREF(thr, tmp); + DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp); } else { tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, desc.e_idx); DUK_TVAL_SET_TVAL(&tv_tmp, tv); @@ -4316,8 +4316,8 @@ DUK_INTERNAL void duk_hobject_define_accessor_internal(duk_hthread *thr, duk_hob DUK_HOBJECT_E_SLOT_SET_ACCESSOR(thr->heap, obj, e_idx); DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, obj, e_idx, getter); DUK_HOBJECT_E_SET_VALUE_SETTER(thr->heap, obj, e_idx, setter); - DUK_HOBJECT_INCREF(thr, getter); - DUK_HOBJECT_INCREF(thr, setter); + DUK_HOBJECT_INCREF_ALLOWNULL(thr, getter); + DUK_HOBJECT_INCREF_ALLOWNULL(thr, setter); } /* @@ -4810,8 +4810,8 @@ void duk_hobject_define_property_helper(duk_context *ctx, DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, obj, e_idx, get); DUK_HOBJECT_E_SET_VALUE_SETTER(thr->heap, obj, e_idx, set); - DUK_HOBJECT_INCREF(thr, get); - DUK_HOBJECT_INCREF(thr, set); + DUK_HOBJECT_INCREF_ALLOWNULL(thr, get); + DUK_HOBJECT_INCREF_ALLOWNULL(thr, set); DUK_HOBJECT_E_SET_FLAGS(thr->heap, obj, e_idx, new_flags); goto success_exotics; @@ -5073,11 +5073,11 @@ void duk_hobject_define_property_helper(duk_context *ctx, tmp = DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, obj, curr.e_idx); DUK_UNREF(tmp); DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, obj, curr.e_idx, NULL); - DUK_HOBJECT_DECREF(thr, tmp); + DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp); tmp = DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, obj, curr.e_idx); DUK_UNREF(tmp); DUK_HOBJECT_E_SET_VALUE_SETTER(thr->heap, obj, curr.e_idx, NULL); - DUK_HOBJECT_DECREF(thr, tmp); + DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp); DUK_TVAL_SET_UNDEFINED_ACTUAL(DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, obj, curr.e_idx)); DUK_HOBJECT_E_SLOT_CLEAR_WRITABLE(thr->heap, obj, curr.e_idx); @@ -5208,8 +5208,8 @@ void duk_hobject_define_property_helper(duk_context *ctx, tmp = DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, obj, curr.e_idx); DUK_UNREF(tmp); DUK_HOBJECT_E_SET_VALUE_SETTER(thr->heap, obj, curr.e_idx, set); - DUK_HOBJECT_INCREF(thr, set); - DUK_HOBJECT_DECREF(thr, tmp); + DUK_HOBJECT_INCREF_ALLOWNULL(thr, set); + DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp); } if (has_get) { duk_hobject *tmp; @@ -5220,8 +5220,8 @@ void duk_hobject_define_property_helper(duk_context *ctx, tmp = DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, obj, curr.e_idx); DUK_UNREF(tmp); DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, obj, curr.e_idx, get); - DUK_HOBJECT_INCREF(thr, get); - DUK_HOBJECT_DECREF(thr, tmp); + DUK_HOBJECT_INCREF_ALLOWNULL(thr, get); + DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp); } if (has_value) { duk_tval *tv1, *tv2; diff --git a/src/duk_hthread_builtins.c b/src/duk_hthread_builtins.c index 743c15da..26dfb854 100644 --- a/src/duk_hthread_builtins.c +++ b/src/duk_hthread_builtins.c @@ -666,6 +666,6 @@ DUK_INTERNAL void duk_hthread_copy_builtin_objects(duk_hthread *thr_from, duk_ht for (i = 0; i < DUK_NUM_BUILTINS; i++) { thr_to->builtins[i] = thr_from->builtins[i]; - DUK_HOBJECT_INCREF(thr_to, thr_to->builtins[i]); /* side effect free */ + DUK_HOBJECT_INCREF_ALLOWNULL(thr_to, thr_to->builtins[i]); /* side effect free */ } } diff --git a/src/duk_hthread_stacks.c b/src/duk_hthread_stacks.c index f15b6529..39beb1b9 100644 --- a/src/duk_hthread_stacks.c +++ b/src/duk_hthread_stacks.c @@ -282,7 +282,7 @@ DUK_INTERNAL void duk_hthread_callstack_unwind(duk_hthread *thr, duk_size_t new_ #endif act->var_env = NULL; #ifdef DUK_USE_REFERENCE_COUNTING - DUK_HOBJECT_DECREF(thr, tmp); + DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp); act = thr->callstack + idx; /* avoid side effect issues */ #endif @@ -291,7 +291,7 @@ DUK_INTERNAL void duk_hthread_callstack_unwind(duk_hthread *thr, duk_size_t new_ #endif act->lex_env = NULL; #ifdef DUK_USE_REFERENCE_COUNTING - DUK_HOBJECT_DECREF(thr, tmp); + DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp); act = thr->callstack + idx; /* avoid side effect issues */ #endif @@ -303,7 +303,7 @@ DUK_INTERNAL void duk_hthread_callstack_unwind(duk_hthread *thr, duk_size_t new_ #endif act->func = NULL; #ifdef DUK_USE_REFERENCE_COUNTING - DUK_HOBJECT_DECREF(thr, tmp); + DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp); act = thr->callstack + idx; /* avoid side effect issues */ DUK_UNREF(act); #endif diff --git a/src/duk_js_call.c b/src/duk_js_call.c index f38faa95..bb9cbd6a 100644 --- a/src/duk_js_call.c +++ b/src/duk_js_call.c @@ -467,8 +467,8 @@ void duk__handle_oldenv_for_call(duk_hthread *thr, act->var_env = act->lex_env; } - DUK_HOBJECT_INCREF(thr, act->lex_env); - DUK_HOBJECT_INCREF(thr, act->var_env); + DUK_HOBJECT_INCREF_ALLOWNULL(thr, act->lex_env); + DUK_HOBJECT_INCREF_ALLOWNULL(thr, act->var_env); } /* @@ -1201,7 +1201,7 @@ duk_int_t duk_handle_call(duk_hthread *thr, * reference reachable through the value stack? If changed, stack * unwind code also needs to be fixed to match. */ - DUK_HOBJECT_INCREF(thr, func); /* act->func */ + DUK_HOBJECT_INCREF_ALLOWNULL(thr, func); /* act->func */ #ifdef DUK_USE_NONSTD_FUNC_CALLER_PROPERTY if (func) { diff --git a/src/duk_js_var.c b/src/duk_js_var.c index deb4b30b..4d8c4343 100644 --- a/src/duk_js_var.c +++ b/src/duk_js_var.c @@ -1671,11 +1671,11 @@ duk_bool_t duk__declvar_helper(duk_hthread *thr, tmp = DUK_HOBJECT_E_GET_VALUE_GETTER(thr->heap, holder, e_idx); DUK_HOBJECT_E_SET_VALUE_GETTER(thr->heap, holder, e_idx, NULL); - DUK_HOBJECT_DECREF(thr, tmp); + DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp); DUK_UNREF(tmp); tmp = DUK_HOBJECT_E_GET_VALUE_SETTER(thr->heap, holder, e_idx); DUK_HOBJECT_E_SET_VALUE_SETTER(thr->heap, holder, e_idx, NULL); - DUK_HOBJECT_DECREF(thr, tmp); + DUK_HOBJECT_DECREF_ALLOWNULL(thr, tmp); DUK_UNREF(tmp); } else { duk_tval tv_tmp;