jww
jww

Reputation: 102245

How to force const propagation through an inline function?

I'm trying to coerce the pre-processor to perform some math for me so a constant gets propagated into inline assembly. Here's the reduced case:

inline
unsigned int RotateRight(unsigned char value, unsigned int amount)
{
    COMPILE_ASSERT(((unsigned char)(amount%32)) < 32);
    __asm__ ("rorb %1, %0" : "+mq" (value) : "I" ((unsigned char)(amount%32)));
    return value;
}

The code above relies upon CPU specific functionality, and I'm OK with it (its actually a template specialization on x86/x64 Linux when GCC is available). The "I" constraint says the integral value must be between [0,31] inclusive.

Callers of the code would look like:

byte b1 = RotateRight(1, 1);
byte b2 = RotateRight(1, 31);

A RotateRight(1, 31) comes from the cryptographers (its undefined behavior in C/C++ because a byte can only be rotated in the range [0,7]). I can break free from C/C++ constraints using ASM. And since the shift amount is known at compile time, I'd like it to be reduced at compile time; and I'd like the rorb version using the immediate-8 generated.

Without the COMPILE_ASSERT, the code compiles but I'm not sure if the constant is being propagated. That is, it might be generated with an unexpected reduction (% 32). With the COMPILE_ASSERT, the code fails to compile.

$ make validat1.o
g++ -DNDEBUG -g2 -O3 -march=native -pipe -c validat1.cpp
In file included from simple.h:10:0,
                 from filters.h:6,
                 from files.h:5,
                 from validat1.cpp:6:
misc.h: In function ‘T CryptoPP::rotlFixed(T, unsigned int) [with T = unsigned char]’:
misc.h:940:43: error: ‘y’ cannot appear in a constant-expression
  CRYPTOPP_COMPILE_ASSERT(((unsigned char)(y%32)) < 32);
                                           ^
misc.h:72:85: note: in definition of macro ‘CRYPTOPP_COMPILE_ASSERT_INSTANCE’
 _COMPILE_ASSERT_INSTANCE(assertion, instance) static CompileAssert<(assertion)>

I know I'm not supposed to use a #define, and C++ inline functions are the answer. But I feel like I'm suffering a disconnect.

How do I force the compiler to propagate the value that involves const values?

Or, if the COMPILE_ASSERT is the wrong tool (const is being propagated), how do I set up a test so that I can verify the immediate-8 version of the rorb is used?


Related, this is a C++03 project. It does not use Boost, does not use Cmake, does not use Autotools, etc.

Upvotes: 3

Views: 706

Answers (1)

stgatilov
stgatilov

Reputation: 5533

When you specify amount as function argument, you lose its compile-time constness.

Why don't you declare amount is template argument? In such case the function user is also forced to pass a compile-time constant, which is good too.

To ensure that shift is used as compile-time constant, you can create a static const local variable.

template<unsigned int amount> inline
unsigned int RotateRight(unsigned char value)
{
    static const unsigned char shift = (unsigned char)(amount%32);
    __asm__ ("rorb %1, %0" : "+mq" (value) : "I" (shift));
    return value;
}

Upvotes: 4

Related Questions