MbedCRC: handle init values better

Init values often need reflection, and use of `__RBIT` prevents constant
init being done at compile time (unless `__RBIT` uses a compiler
intrinsic, which it doesn't for GCC).

Rather than try to handle constants 0U and -1U with a special case to
avoid the RBIT, which can in turn lead to runtime bloat for nonconstant
inits, use a C++20 style is_constant_evaluated() check to switch between
C and assembly forms.

This reduces code-size for non-constant init, by eliminating a runtime
condition, and allows the bit-reversal of any constant init to happen at
compile time.
pull/11897/head
Kevin Bracey 2019-11-19 15:28:08 +02:00
parent 07d43b72d2
commit 094f22901f
1 changed files with 102 additions and 75 deletions

View File

@ -29,7 +29,12 @@
#include "platform/SingletonPtr.h"
#include "platform/PlatformMutex.h"
#ifdef UNITTEST
#include <type_traits>
#define MSTD_CONSTEXPR_IF_HAS_IS_CONSTANT_EVALUATED
#else
#include <mstd_type_traits>
#endif
namespace mbed {
/** \addtogroup drivers-public-api */
@ -175,11 +180,13 @@ public:
* polynomials with different initial/final/reflect values
*
*/
constexpr
MbedCRC(uint32_t initial_xor, uint32_t final_xor, bool reflect_data, bool reflect_remainder) :
crc_impl(initial_xor, final_xor, reflect_data, reflect_remainder)
{
}
constexpr
MbedCRC();
/** Compute CRC for the data input
@ -279,6 +286,7 @@ class MbedCRC {
public:
typedef size_t crc_data_size_t;
constexpr
MbedCRC(uint32_t initial_xor, uint32_t final_xor, bool reflect_data, bool reflect_remainder) :
_initial_value(adjust_initial_value(initial_xor, reflect_data)),
_final_xor(final_xor),
@ -400,7 +408,7 @@ public:
}
} else {
/* CRC has MSB in top bit of register */
p_crc = _reflect_remainder ? reflect_register(p_crc) : shift_right(p_crc);
p_crc = _reflect_remainder ? reflect(p_crc) : shift_right(p_crc);
}
} else { // TABLE
/* CRC has MSB in bottom bit of register */
@ -417,45 +425,91 @@ public:
}
private:
/** Guaranteed constexpr reflection (all toolchains)
*
* @note This should never be run-time evaluated - very inefficient
* @param Register value to be reflected (full 32-bit value)
* @return Reflected value (full 32-bit value)
*/
static constexpr uint32_t reflect_constant(uint32_t data)
{
/* Doing this hard way to keep it C++11 constexpr and hence ARM C 5 compatible */
return ((data & 0x00000001) << 31) |
((data & 0x00000002) << 29) |
((data & 0x00000004) << 27) |
((data & 0x00000008) << 25) |
((data & 0x00000010) << 23) |
((data & 0x00000020) << 21) |
((data & 0x00000040) << 19) |
((data & 0x00000080) << 17) |
((data & 0x00000100) << 15) |
((data & 0x00000200) << 13) |
((data & 0x00000400) << 11) |
((data & 0x00000800) << 9) |
((data & 0x00001000) << 7) |
((data & 0x00002000) << 5) |
((data & 0x00004000) << 3) |
((data & 0x00008000) << 1) |
((data & 0x00010000) >> 1) |
((data & 0x00020000) >> 3) |
((data & 0x00040000) >> 5) |
((data & 0x00080000) >> 7) |
((data & 0x00100000) >> 9) |
((data & 0x00200000) >> 11) |
((data & 0x00400000) >> 13) |
((data & 0x00800000) >> 15) |
((data & 0x01000000) >> 17) |
((data & 0x02000000) >> 19) |
((data & 0x04000000) >> 21) |
((data & 0x08000000) >> 23) |
((data & 0x10000000) >> 25) |
((data & 0x20000000) >> 27) |
((data & 0x40000000) >> 29) |
((data & 0x80000000) >> 31);
}
/** General reflection
*
* @note This is used when we may need to perform run-time computation, so
* we need the possibility to produce the optimal run-time RBIT instruction. But
* if the compiler doesn't treat RBIT as a built-in, it's useful to have a C fallback
* for the constant case, avoiding runtime RBIT(0) computations. This is an
* optimization only available for some toolchains; others will always use runtime
* RBIT. If we require a constant expression, use reflect_constant instead.
*
* @param Register value to be reflected (full 32-bit value)
* @return Reflected value (full 32-bit value)
*/
#ifdef MSTD_HAS_IS_CONSTANT_EVALUATED
static constexpr uint32_t reflect(uint32_t data)
{
return mstd::is_constant_evaluated() ? reflect_constant(data) : __RBIT(data);
}
#else
static uint32_t reflect(uint32_t data)
{
return __RBIT(data);
}
#endif
/** Data bytes may need to be reflected.
*
* @param data value to be reflected (bottom 8 bits)
* @return Reflected value (bottom 8 bits)
*/
static MSTD_CONSTEXPR_IF_HAS_IS_CONSTANT_EVALUATED
uint_fast32_t reflect_byte(uint_fast32_t data)
{
return reflect(data) >> 24;
}
/** Get the current CRC polynomial, reflected at bottom of register.
*
* @return Reflected polynomial value (so x^width term would be at bit -1)
*/
static constexpr uint32_t get_reflected_polynomial()
{
/* Doing this hard way to keep it C++11 constexpr and hence ARM C 5 compatible */
return shift_right(((polynomial & 0x00000001) << 31) |
((polynomial & 0x00000002) << 29) |
((polynomial & 0x00000004) << 27) |
((polynomial & 0x00000008) << 25) |
((polynomial & 0x00000010) << 23) |
((polynomial & 0x00000020) << 21) |
((polynomial & 0x00000040) << 19) |
((polynomial & 0x00000080) << 17) |
((polynomial & 0x00000100) << 15) |
((polynomial & 0x00000200) << 13) |
((polynomial & 0x00000400) << 11) |
((polynomial & 0x00000800) << 9) |
((polynomial & 0x00001000) << 7) |
((polynomial & 0x00002000) << 5) |
((polynomial & 0x00004000) << 3) |
((polynomial & 0x00008000) << 1) |
((polynomial & 0x00010000) >> 1) |
((polynomial & 0x00020000) >> 3) |
((polynomial & 0x00040000) >> 5) |
((polynomial & 0x00080000) >> 7) |
((polynomial & 0x00100000) >> 9) |
((polynomial & 0x00200000) >> 11) |
((polynomial & 0x00400000) >> 13) |
((polynomial & 0x00800000) >> 15) |
((polynomial & 0x01000000) >> 17) |
((polynomial & 0x02000000) >> 19) |
((polynomial & 0x04000000) >> 21) |
((polynomial & 0x08000000) >> 23) |
((polynomial & 0x10000000) >> 25) |
((polynomial & 0x20000000) >> 27) |
((polynomial & 0x40000000) >> 29) |
((polynomial & 0x80000000) >> 31));
return shift_right(reflect_constant(polynomial));
}
/** Get the current CRC polynomial, at top of register.
@ -484,21 +538,8 @@ private:
static const crc_table_t _crc_table[MBED_CRC_TABLE_SIZE];
#endif
static uint32_t adjust_initial_value(uint32_t initial_xor, bool reflect_data)
static constexpr uint32_t adjust_initial_value(uint32_t initial_xor, bool reflect_data)
{
/* As initial_xor is almost certain to be constant all zeros or ones, try to
* process that a constant, avoiding an RBIT instruction (or worse).
*/
if (initial_xor == 0 || initial_xor == (get_crc_mask() & -1U)) {
/* Only possible adjustment is shifting to top for bitwise */
if (mode == CrcMode::BITWISE && !reflect_data) {
return shift_left(initial_xor);
} else {
return initial_xor;
}
}
/* Weird or non-constant initial value - need to think about reflection */
if (mode == CrcMode::BITWISE) {
/* For bitwise calculation, CRC register is reflected if data is, to match input.
* (MSB at bottom of register). If not reflected, it is at the top of the register
@ -545,34 +586,15 @@ private:
return (uint32_t)((uint32_t)2U << (width - 1)) - 1U;
}
/** Data bytes may need to be reflected.
*
* @param data value to be reflected (bottom 8 bits)
* @return Reflected value (bottom 8 bits)
*/
static uint_fast32_t reflect_byte(uint_fast32_t data)
{
return __RBIT(data) >> 24;
}
/** CRC values may need to be reflected.
*
* @param CRC value to be reflected (width bits at bottom of 32-bit word)
* @return Reflected value (still at bottom of 32-bit word)
*/
static uint32_t reflect_crc(uint32_t data)
static MSTD_CONSTEXPR_IF_HAS_IS_CONSTANT_EVALUATED
uint32_t reflect_crc(uint32_t data)
{
return __RBIT(data) >> (32 - width);
}
/** Register values may need to be reflected.
*
* @param Register value to be reflected (full 32-bit value)
* @return Reflected value (full 32-bit value)
*/
static uint32_t reflect_register(uint32_t data)
{
return __RBIT(data);
return reflect(data) >> (32 - width);
}
/** Register values may need to be shifted left.
@ -823,27 +845,32 @@ const uint32_t MbedCRC<POLY_32BIT_ANSI, 32, CrcMode::TABLE>::_crc_table[MBED_CRC
/* Default values for different types of polynomials
*/
template<>
inline MbedCRC<POLY_32BIT_ANSI, 32>::MbedCRC() : MbedCRC(0xFFFFFFFF, 0xFFFFFFFF, true, true)
inline MSTD_CONSTEXPR_FN_14
MbedCRC<POLY_32BIT_ANSI, 32>::MbedCRC() : MbedCRC(0xFFFFFFFF, 0xFFFFFFFF, true, true)
{
}
template<>
inline MbedCRC<POLY_16BIT_IBM, 16>::MbedCRC() : MbedCRC(0, 0, true, true)
inline MSTD_CONSTEXPR_FN_14
MbedCRC<POLY_16BIT_IBM, 16>::MbedCRC() : MbedCRC(0, 0, true, true)
{
}
template<>
inline MbedCRC<POLY_16BIT_CCITT, 16>::MbedCRC() : MbedCRC(0xFFFF, 0, false, false)
inline MSTD_CONSTEXPR_FN_14
MbedCRC<POLY_16BIT_CCITT, 16>::MbedCRC() : MbedCRC(0xFFFF, 0, false, false)
{
}
template<>
inline MbedCRC<POLY_7BIT_SD, 7>::MbedCRC(): MbedCRC(0, 0, false, false)
inline MSTD_CONSTEXPR_FN_14
MbedCRC<POLY_7BIT_SD, 7>::MbedCRC(): MbedCRC(0, 0, false, false)
{
}
template<>
inline MbedCRC<POLY_8BIT_CCITT, 8>::MbedCRC(): MbedCRC(0, 0, false, false)
inline MSTD_CONSTEXPR_FN_14
MbedCRC<POLY_8BIT_CCITT, 8>::MbedCRC(): MbedCRC(0, 0, false, false)
{
}