I have the following situation: there's a huge set of templates like std::vector
that will call memmove()
to move parts of array. Sometimes they will want to "move" parts of length zero - for example, if the array tail is removed (like std::vector::erase()
), they will want to move the remainder of the array which will happen to have length zero and that zero will be known at compile time (I saw the disassembly - the compiler is aware) yet the compiler will still emit a memmove()
call.
So basically I could have a wrapper:
inline void callMemmove( void* dest, const void* source, size_t count )
{
if( count > 0 ) {
memmove( dest, source, count );
}
}
but this would introduce an extra runtime check in cases count
is not known in compile time that I don't want.
Is it somehow possible to use __assume hint to indicate to the compiler that if it knows for sure that count
is zero it should eliminate the memmove()
?
The point of the __assume
is to tell the compiler to skip portions of code when optimizing. In the link you provided the example is given with the default
clause of the switch
construct - there the hint tells the compiler that the clause will never be reached even though theoretically it could. You're telling the optimizer, basically, "Hey, I know better, throw this code away".
For default
you can't not write it in (unless you cover the whole range in case
s, which is sometimes problematic) because it would cause compilation error. So you need the hint to optimize the code you know that is unneeded out.
In your case - the code can be reached, but not always, so the __assume
hint won't help you much. You have to check if the count
is really 0. Unless you're sure it can never be anything but 0, then just don't write it in.
This solution uses a trick described in C++ compile-time constant detection - the trick uses the fact compile time integer zero can be converted to a pointer, and this can be used together with overloading to check for the "compile time known" property.
struct chkconst {
struct Small {char a;};
struct Big: Small {char b;};
struct Temp { Temp( int x ) {} };
static Small chk2( void* ) { return Small(); }
static Big chk2( Temp ) { return Big(); }
};
#define is_const_0(X) (sizeof(chkconst::chk2(X))<sizeof(chkconst::Big))
#define is_const(X) is_const_0( int(X)-int(X) )
#define memmove_smart(dst,src,n) do { \
if (is_const(n)) {if (n>0) memmove(dst,src,n);} \
else memmove(dst,src,n); \
} while (false)
Or, in your case, as you want to check for zero only anyway, one could use is_const_0 directly for maximum simplicity and portability:
#define memmove_smart(dst,src,n) if (is_const_0(n)) {} else memmove(dst,src,n)
Note: the code here used a version of is_const simpler than in the linked question. This is because Visual Studio is more standard conformant than GCC in this case. If targeting gcc, you could use following is_const variant (adapted to handle all possible integral values, including negative and INT_MAX):
#define is_const_0(X) (sizeof(chkconst::chk2(X))<sizeof(chkconst::Big))
#define is_const_pos(X) is_const_0( int(X)^(int(X)&INT_MAX) )
#define is_const(X) (is_const_pos(X)|is_const_pos(-int(X))|is_const_pos(-(int(X)+1)))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With