diff options
author | David Mitchell <davem@iabyn.com> | 2015-02-26 18:42:16 +0000 |
---|---|---|
committer | David Mitchell <davem@iabyn.com> | 2015-03-03 21:36:15 +0000 |
commit | e6bdf523c482703fe07cfe70c76a0efbd6971301 (patch) | |
tree | 5e9311620852b8be8e90ae9c78107f69cd71bf06 /handy.h | |
parent | c47e32e38f12c56c3098cb7845b451787467f03c (diff) | |
download | perl-e6bdf523c482703fe07cfe70c76a0efbd6971301.tar.gz |
Make MEM_WRAP_CHECK more compile-time
MEM_WRAP_CHECK(n,t) checks whether n * sizeof(t) exceeds the
memory size, and so is likely to wrap.
When the type of n is small (e.g. a U8), you used to get compiler warnings
about a comparison always being true. This was avoided by adding 0.0. Now
Coverity complains that you're doing a floating-point comparison with the
results of an integer division.
Instead of adding 0.0, instead add some more compile-time checks
that will cause the runtime check to be skipped when the maximum value
of n (as determined by sizeof(n)) is a lot less than memory size.
On my 64-bit system this also pleasingly makes the executable 8384 bytes
smaller, implying that in many cases, the run-time check is now being
skipped.
Diffstat (limited to 'handy.h')
-rw-r--r-- | handy.h | 44 |
1 files changed, 37 insertions, 7 deletions
@@ -1876,14 +1876,44 @@ PoisonWith(0xEF) for catching access to freed memory. #define MEM_SIZE_MAX ((MEM_SIZE)~0) -/* The +0.0 in MEM_WRAP_CHECK_ is an attempt to foil - * overly eager compilers that will bleat about e.g. - * (U16)n > (size_t)~0/sizeof(U16) always being false. */ + #ifdef PERL_MALLOC_WRAP -#define MEM_WRAP_CHECK(n,t) \ - (void)(UNLIKELY(sizeof(t) > 1 && ((MEM_SIZE)(n)+0.0) > MEM_SIZE_MAX/sizeof(t)) && (croak_memory_wrap(),0)) -#define MEM_WRAP_CHECK_1(n,t,a) \ - (void)(UNLIKELY(sizeof(t) > 1 && ((MEM_SIZE)(n)+0.0) > MEM_SIZE_MAX/sizeof(t)) && (Perl_croak_nocontext("%s",(a)),0)) + +/* This expression will be constant-folded at compile time. It checks + * whether or not the type of the count n is so small (e.g. U8 or U16, or + * U32 on 64-bit systems) that there's no way a wrap-around could occur. + * As well as avoiding the need for a run-time check in some cases, it's + * designed to avoid compiler warnings like: + * comparison is always false due to limited range of data type + */ + +# define _MEM_WRAP_NEEDS_RUNTIME_CHECK(n,t) \ + (sizeof(t) > ((MEM_SIZE)1 << 8*(sizeof(MEM_SIZE) - sizeof(n)))) + +/* this is written in a slightly odd way because for an expression like + * cond && (n > expr) + * even when cond constant-folds to false at compile-time, g++ insists + * on emitting warnings about expr (e.g. "comparison is always false"). + * So rewrite it as + * (cond ? n : 1) > expr + * + * We happen to know that (1 > expr) will always be false (unless someone + * is doing something with a struct whose sizeof > MEM_SIZE_MAX/2), so + * this is safe. + */ + +# define _MEM_WRAP_WILL_WRAP(n,t) \ + ((MEM_SIZE)(_MEM_WRAP_NEEDS_RUNTIME_CHECK(n,t) ? n : 1) \ + > MEM_SIZE_MAX/sizeof(t)) + +# define MEM_WRAP_CHECK(n,t) \ + (void)(UNLIKELY(_MEM_WRAP_WILL_WRAP(n,t)) \ + && (croak_memory_wrap(),0)) + +# define MEM_WRAP_CHECK_1(n,t,a) \ + (void)(UNLIKELY(_MEM_WRAP_WILL_WRAP(n,t)) \ + && (Perl_croak_nocontext("%s",(a)),0)) + #define MEM_WRAP_CHECK_(n,t) MEM_WRAP_CHECK(n,t), #define PERL_STRLEN_ROUNDUP(n) ((void)(((n) > MEM_SIZE_MAX - 2 * PERL_STRLEN_ROUNDUP_QUANTUM) ? (croak_memory_wrap(),0):0),((n-1+PERL_STRLEN_ROUNDUP_QUANTUM)&~((MEM_SIZE)PERL_STRLEN_ROUNDUP_QUANTUM-1))) |