make constexpr variants for SkCLZ/SkCLT and similar functions

Make a parallel constexpr set of functions for bit log and power
functions. These are labeled as *_portable.

Change-Id: I40cca37eb3e75c496638d8b58d622467dd692b27
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/333657
Commit-Queue: Herb Derby <herb@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
This commit is contained in:
Herb Derby 2020-11-10 14:20:52 -05:00 committed by Skia Commit-Bot
parent 7d1153fc4c
commit 7a1928ff43

View File

@ -143,45 +143,54 @@ constexpr int SkCLZ_portable(uint32_t x) {
return n - x;
}
#ifndef SkCLZ
#if defined(SK_BUILD_FOR_WIN)
#include <intrin.h>
static_assert(32 == SkCLZ_portable(0));
static_assert(31 == SkCLZ_portable(1));
static_assert( 1 == SkCLZ_portable(1 << 30));
static_assert( 1 == SkCLZ_portable((1 << 30) | (1 << 24) | 1));
static_assert( 0 == SkCLZ_portable(~0U));
constexpr int SkCLZ(uint32_t mask) {
if (mask) {
unsigned long index = 0;
_BitScanReverse(&index, mask);
// Suppress this bogus /analyze warning. The check for non-zero
// guarantees that _BitScanReverse will succeed.
#pragma warning(suppress : 6102) // Using 'index' from failed function call
return index ^ 0x1F;
} else {
return 32;
}
#if defined(SK_BUILD_FOR_WIN)
#include <intrin.h>
static inline int SkCLZ(uint32_t mask) {
if (mask) {
unsigned long index = 0;
_BitScanReverse(&index, mask);
// Suppress this bogus /analyze warning. The check for non-zero
// guarantees that _BitScanReverse will succeed.
#pragma warning(suppress : 6102) // Using 'index' from failed function call
return index ^ 0x1F;
} else {
return 32;
}
#elif defined(SK_CPU_ARM32) || defined(__GNUC__) || defined(__clang__)
constexpr int SkCLZ(uint32_t mask) {
// __builtin_clz(0) is undefined, so we have to detect that case.
return mask ? __builtin_clz(mask) : 32;
}
#else
constexpr int SkCLZ(uint32_t mask) {
return SkCLZ_portable(mask);
}
#endif
}
#elif defined(SK_CPU_ARM32) || defined(__GNUC__) || defined(__clang__)
static inline int SkCLZ(uint32_t mask) {
// __builtin_clz(0) is undefined, so we have to detect that case.
return mask ? __builtin_clz(mask) : 32;
}
#else
static inline int SkCLZ(uint32_t mask) {
return SkCLZ_portable(mask);
}
#endif
//! Returns the number of trailing zero bits (0...32)
// From Hacker's Delight 2nd Edition
constexpr int SkCTZ_portable(uint32_t x) {
return 32 - SkCLZ(~x & (x - 1));
return 32 - SkCLZ_portable(~x & (x - 1));
}
#ifndef SkCTZ
#if defined(SK_BUILD_FOR_WIN)
static_assert(32 == SkCTZ_portable(0));
static_assert( 0 == SkCTZ_portable(1));
static_assert(30 == SkCTZ_portable(1 << 30));
static_assert( 2 == SkCTZ_portable((1 << 30) | (1 << 24) | (1 << 2)));
static_assert( 0 == SkCTZ_portable(~0U));
#if defined(SK_BUILD_FOR_WIN)
#include <intrin.h>
constexpr int SkCTZ(uint32_t mask) {
static inline int SkCTZ(uint32_t mask) {
if (mask) {
unsigned long index = 0;
_BitScanForward(&index, mask);
@ -194,36 +203,15 @@ constexpr int SkCTZ_portable(uint32_t x) {
}
}
#elif defined(SK_CPU_ARM32) || defined(__GNUC__) || defined(__clang__)
constexpr int SkCTZ(uint32_t mask) {
static inline int SkCTZ(uint32_t mask) {
// __builtin_ctz(0) is undefined, so we have to detect that case.
return mask ? __builtin_ctz(mask) : 32;
}
#else
constexpr int SkCTZ(uint32_t mask) {
static inline int SkCTZ(uint32_t mask) {
return SkCTZ_portable(mask);
}
#endif
#endif
/**
* Returns the smallest power-of-2 that is >= the specified value. If value
* is already a power of 2, then it is returned unchanged. It is undefined
* if value is <= 0.
*/
constexpr int SkNextPow2(int value) {
SkASSERT(value > 0);
return 1 << (32 - SkCLZ(value - 1));
}
/**
* Returns the largest power-of-2 that is <= the specified value. If value
* is already a power of 2, then it is returned unchanged. It is undefined
* if value is <= 0.
*/
constexpr int SkPrevPow2(int value) {
SkASSERT(value > 0);
return 1 << (32 - SkCLZ(value >> 1));
}
/**
* Returns the log2 of the specified value, were that value to be rounded up
@ -234,11 +222,16 @@ constexpr int SkPrevPow2(int value) {
* SkNextLog2(4) -> 2
* SkNextLog2(5) -> 3
*/
constexpr int SkNextLog2(uint32_t value) {
static inline int SkNextLog2(uint32_t value) {
SkASSERT(value != 0);
return 32 - SkCLZ(value - 1);
}
constexpr int SkNextLog2_portable(uint32_t value) {
SkASSERT(value != 0);
return 32 - SkCLZ_portable(value - 1);
}
/**
* Returns the log2 of the specified value, were that value to be rounded down
* to the previous power of 2. It is undefined to pass 0. Examples:
@ -248,11 +241,46 @@ constexpr int SkNextLog2(uint32_t value) {
* SkPrevLog2(4) -> 2
* SkPrevLog2(5) -> 2
*/
constexpr int SkPrevLog2(uint32_t value) {
static inline int SkPrevLog2(uint32_t value) {
SkASSERT(value != 0);
return 32 - SkCLZ(value >> 1);
}
constexpr int SkPrevLog2_portable(uint32_t value) {
SkASSERT(value != 0);
return 32 - SkCLZ_portable(value >> 1);
}
/**
* Returns the smallest power-of-2 that is >= the specified value. If value
* is already a power of 2, then it is returned unchanged. It is undefined
* if value is <= 0.
*/
static inline int SkNextPow2(int value) {
SkASSERT(value > 0);
return 1 << SkNextLog2(value);
}
constexpr int SkNextPow2_portable(int value) {
SkASSERT(value > 0);
return 1 << SkNextLog2_portable(value);
}
/**
* Returns the largest power-of-2 that is <= the specified value. If value
* is already a power of 2, then it is returned unchanged. It is undefined
* if value is <= 0.
*/
static inline int SkPrevPow2(int value) {
SkASSERT(value > 0);
return 1 << SkPrevLog2(value);
}
constexpr int SkPrevPow2_portable(int value) {
SkASSERT(value > 0);
return 1 << SkPrevLog2_portable(value);
}
///////////////////////////////////////////////////////////////////////////////
/**