diff --git a/core/include/kernel_defines.h b/core/include/kernel_defines.h index 12f3ea410b..5bb77a0108 100644 --- a/core/include/kernel_defines.h +++ b/core/include/kernel_defines.h @@ -236,6 +236,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) +#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 */ diff --git a/tests/unittests/tests-kernel_defines/tests-kernel_defines.c b/tests/unittests/tests-kernel_defines/tests-kernel_defines.c index 6d63ee981c..5ea45f820d 100644 --- a/tests/unittests/tests-kernel_defines/tests-kernel_defines.c +++ b/tests/unittests/tests-kernel_defines/tests-kernel_defines.c @@ -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);