1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-01-17 05:32:45 +01:00

core: Add IS_CT_CONSTANT()

This adds a simple macro to check (at C level) whether a given
expression is proven to be compile time constant and suitable for
constant folding. This allows writing code like this:

```C
int gpio_read(gpio_t pin) {
    if (IS_CT_CONSTANT(pin)) {
        /* this implementation should even be able to use the port and
         * pin number as immediate in inline assembly */
    }
    else {
        /* less efficient implementation that cannot use port and pin
         * number as immediate in inline assembly */
    }
}
```
This commit is contained in:
Marian Buschsieweke 2021-11-25 11:22:22 +01:00
parent 0de8bfaadc
commit 68424a924c
No known key found for this signature in database
GPG Key ID: CB8E3238CE715A94
2 changed files with 45 additions and 0 deletions

View File

@ -256,6 +256,27 @@ extern "C" {
#define DECLARE_CONSTANT(identifier, const_expr) \
WITHOUT_PEDANTIC(enum { identifier = const_expr };)
#if DOXYGEN
/**
* @brief Check if given variable / expression is detected as compile time
* constant
* @note This might return 0 on compile time constant expressions if the
* compiler is not able to prove the constness at the given level
* of optimization.
* @details This will return 0 if the used compiler does not support this
* @warning This is intended for internal use only
*
* This allows providing two different implementations in C, with one being
* more efficient if constant folding is used.
*/
#define IS_CT_CONSTANT(expr) <IMPLEMENTATION>
#elif defined(__GNUC__)
/* both clang and gcc (which both define __GNUC__) support this */
#define IS_CT_CONSTANT(expr) __builtin_constant_p(expr)
#else
#define IS_CT_CONSTANT(expr) 0
#endif
/**
* @cond INTERNAL
*/

View File

@ -77,12 +77,36 @@ static void test_declare_constant(void)
static_assert(sizeof(test_array) == 3, "test_array should be 3 bytes long");
}
#ifdef BOARD_NATIVE
/* native compiles with -Og, which does not automatically inline functions.
* We just turn the function into a macro to get the test also passing on
* native */
#define magic_computation(...) (unsigned)(42U * 3.14159 / 1337U)
#else
static unsigned magic_computation(void)
{
return (unsigned)(42U * 3.14159 / 1337U);
}
#endif
static void test_is_compile_time_constant(void)
{
/* These test might fail on non-GCC-non-clang compilers. We don't support
* any of those (yet), but this test might need adaption in the future */
unsigned actual_constant = magic_computation();
volatile unsigned not_a_constant = actual_constant;
TEST_ASSERT(IS_CT_CONSTANT(actual_constant));
TEST_ASSERT(!IS_CT_CONSTANT(not_a_constant));
}
Test *tests_kernel_defines_tests(void)
{
EMB_UNIT_TESTFIXTURES(fixtures) {
new_TestFixture(test_kernel_version),
new_TestFixture(test_index_of),
new_TestFixture(test_declare_constant),
new_TestFixture(test_is_compile_time_constant),
};
EMB_UNIT_TESTCALLER(kernel_defines_tests, NULL, NULL, fixtures);