Skip to content
Permalink
Browse files

stdint.h: streamline type definitions

Compilers (at least gcc and clang) already provide definitions to
create standard types and their range. For example, __INT16_TYPE__ is
normally defined as a short to be used with the int16_t typedef, and
__INT16_MAX__ is defined as 32767. So it makes sense to rely on them
rather than hardcoding our own, especially for the fast types where
the compiler itself knows what basic type is best.

Using compiler provided definitions makes even more sense when dealing
with 64-bit targets where some types such as intptr_t and size_t must
have a different size and range. Those definitions are then adjusted
by the compiler directly.

However there are two cases for which we should override those
definitions:

* The __INT32_TYPE__ definition on 32-bit targets vary between an int
  and a long int depending on the architecture and configuration.
  Notably, all compilers shipped with the Zephyr SDK, except for the
  i586-zephyr-elfiamcu variant, define __INT32_TYPE__ to a long int.
  Whereas, all Linux configurations for gcc, both 32-bit and 64-bit,
  always define __INT32_TYPE__ as an int. Having variability here is
  not welcome as pointers to a long int and to an int are not deemed
  compatible by the compiler, and printing an int32_t defined with a
  long using %d makes the compiler to complain, even if they're the
  same size on 32-bit targets. Given that an int is always 32 bits
  on all targets we might care about, and given that Zephyr hardcoded
  int32_t to an int before, then we just redefine __INT32_TYPE__ and
  derrivatives to an int to keep the peace in the code.

* The confusion also exists with __INTPTR_TYPE__. Looking again at the
  Zephyr SDK, it is defined as an int, even even when __INT32_TYPE__ is
  initially a long int. One notable exception is i586-zephyr-elf where
  __INTPTR_TYPE__ is a long int even when using -m32. On 64-bit targets
  this is always a long int. So let's redefine __INTPTR_TYPE__ to always
  be a long int on Zephyr which simplifies the code, works for both
  32-bit and 64-bit targets, and mimics what the Linux kernel does.
  Only a few print format strings needed adjustment.

In those two cases, there is a safeguard to ensure the type we're
enforcing has the right size and fail the build otherwise.

Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
  • Loading branch information...
Nicolas Pitre authored and nashif committed Jun 5, 2019
1 parent f57ba2d commit f32330b22c333012cf5f771b0eae9ef378e446d4
@@ -220,6 +220,9 @@ toolchain_cc_nostdinc()
# @Intent: Set compiler specific macro inclusion of AUTOCONF_H
toolchain_cc_imacros(${AUTOCONF_H})

# @Intent: Enforce standard integer type correspondance to match Zephyr usage.
toolchain_cc_imacros(${ZEPHYR_BASE}/include/toolchain/zephyr_stdint.h)

# @Intent: Set compiler specific flag for bare metal freestanding option
toolchain_cc_freestanding()

@@ -175,7 +175,7 @@ void z_arch_configure_dynamic_mpu_regions(struct k_thread *thread)
*/
continue;
}
LOG_DBG("set region 0x%x 0x%x",
LOG_DBG("set region 0x%lx 0x%x",
partition.start, partition.size);
__ASSERT(region_num < _MAX_DYNAMIC_MPU_REGIONS_NUM,
"Out-of-bounds error for dynamic region map.");
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2019 BayLibre SAS
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_INCLUDE_TOOLCHAIN_STDINT_H_
#define ZEPHYR_INCLUDE_TOOLCHAIN_STDINT_H_

/*
* Some gcc versions and/or configurations as found in the Zephyr SDK
* (questionably) define __INT32_TYPE__ and derrivatives as a long int
* which makes the printf format checker to complain about long vs int
* mismatch when %u is given a u32_t argument, and u32_t pointers not
* being compatible with int pointers. Let's redefine them to follow
* common expectations and usage.
*/

#if __SIZEOF_INT__ != 4
#error "unexpected int width"
#endif

#undef __INT32_TYPE__
#undef __UINT32_TYPE__
#undef __INT_LEAST32_TYPE__
#undef __UINT_LEAST32_TYPE__
#define __INT32_TYPE__ int
#define __UINT32_TYPE__ unsigned int
#define __INT_LEAST32_TYPE__ __INT32_TYPE__
#define __UINT_LEAST32_TYPE__ __UINT32_TYPE__

/*
* The confusion also exists with __INTPTR_TYPE__ which is either an int
* (even when __INT32_TYPE__ is a long int) or a long int. Let's redefine
* it to a long int to get some uniformity. Doing so also makes it compatible
* with LP64 (64-bit) targets where a long is always 64-bit wide.
*/

#if __SIZEOF_POINTER__ != __SIZEOF_LONG__
#error "unexpected size difference between pointers and long ints"
#endif

#undef __INTPTR_TYPE__
#undef __UINTPTR_TYPE__
#define __INTPTR_TYPE__ long int
#define __UINTPTR_TYPE__ long unsigned int

#endif /* ZEPHYR_INCLUDE_TOOLCHAIN_STDINT_H_ */
@@ -31,7 +31,7 @@ static bool sane_partition(const struct k_mem_partition *part,

if (exec && write) {
__ASSERT(false,
"partition is writable and executable <start %x>",
"partition is writable and executable <start %lx>",
part->start);
return false;
}
@@ -59,7 +59,7 @@ static bool sane_partition(const struct k_mem_partition *part,
if ((cur_write && exec) || (cur_exec && write)) {
__ASSERT(false, "overlapping partitions are "
"writable and executable "
"<%x...%x>, <%x...%x>",
"<%lx...%x>, <%lx...%x>",
part->start, last,
parts[i].start, cur_last);
return false;
@@ -14,36 +14,36 @@
#define PRId16 "d" /* int16_t */
#define PRId32 "d" /* int32_t */
#define PRId64 "lld" /* int64_t */
#define PRIdPTR "d" /* intptr_t */
#define PRIdPTR "ld" /* intptr_t */

#define PRIi8 "i" /* int8_t */
#define PRIi16 "i" /* int16_t */
#define PRIi32 "i" /* int32_t */
#define PRIi64 "lli" /* int64_t */
#define PRIiPTR "i" /* intptr_t */
#define PRIiPTR "li" /* intptr_t */

#define PRIo8 "o" /* int8_t */
#define PRIo16 "o" /* int16_t */
#define PRIo32 "o" /* int32_t */
#define PRIo64 "llo" /* int64_t */
#define PRIoPTR "o" /* intptr_t */
#define PRIoPTR "lo" /* intptr_t */

#define PRIu8 "u" /* uint8_t */
#define PRIu16 "u" /* uint16_t */
#define PRIu32 "u" /* uint32_t */
#define PRIu64 "llu" /* uint64_t */
#define PRIuPTR "u" /* uintptr_t */
#define PRIuPTR "lu" /* uintptr_t */

#define PRIx8 "x" /* uint8_t */
#define PRIx16 "x" /* uint16_t */
#define PRIx32 "x" /* uint32_t */
#define PRIx64 "llx" /* uint64_t */
#define PRIxPTR "x" /* uintptr_t */
#define PRIxPTR "lx" /* uintptr_t */

#define PRIX8 "X" /* uint8_t */
#define PRIX16 "X" /* uint16_t */
#define PRIX32 "X" /* uint32_t */
#define PRIX64 "llX" /* uint64_t */
#define PRIXPTR "X" /* uintptr_t */
#define PRIXPTR "lX" /* uintptr_t */

#endif
@@ -13,64 +13,62 @@
extern "C" {
#endif

#define INT8_MAX 0x7F
#define INT16_MAX 0x7FFF
#define INT32_MAX 0x7FFFFFFF
#define INT64_MAX 0x7FFFFFFFFFFFFFFFLL
#define INT8_MAX __INT8_MAX__
#define INT16_MAX __INT16_MAX__
#define INT32_MAX __INT32_MAX__
#define INT64_MAX __INT64_MAX__

#define INT8_MIN (-INT8_MAX - 1)
#define INT16_MIN (-INT16_MAX - 1)
#define INT32_MIN (-INT32_MAX - 1)
#define INT64_MIN (-INT64_MAX - 1LL)

#define UINT8_MAX 0xFF
#define UINT16_MAX 0xFFFF
#define UINT32_MAX 0xFFFFFFFFU
#define UINT64_MAX 0xFFFFFFFFFFFFFFFFULL
#define UINT8_MAX __UINT8_MAX__
#define UINT16_MAX __UINT16_MAX__
#define UINT32_MAX __UINT32_MAX__
#define UINT64_MAX __UINT64_MAX__

#define INTPTR_MIN INT32_MIN
#define INTPTR_MAX INT32_MAX
#define UINTPTR_MAX UINT32_MAX
#define INTPTR_MAX __INTPTR_MAX__
#define INTPTR_MIN (-INTPTR_MAX - 1)
#define UINTPTR_MAX __UINTPTR_MAX__

#define PTRDIFF_MIN INT32_MIN
#define PTRDIFF_MAX INT32_MAX
#define PTRDIFF_MAX __PTRDIFF_MAX__
#define PTRDIFF_MIN (-PTRDIFF_MAX - 1)

#define SIZE_MAX UINT32_MAX
#define SIZE_MAX __SIZE_MAX__

typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
typedef signed long long int64_t;
typedef __INT8_TYPE__ int8_t;
typedef __INT16_TYPE__ int16_t;
typedef __INT32_TYPE__ int32_t;
typedef __INT64_TYPE__ int64_t;

/* Assume int to be the fastest type for all types except 64bit ones */
typedef __INT_FAST8_TYPE__ int_fast8_t;
typedef __INT_FAST16_TYPE__ int_fast16_t;
typedef __INT_FAST32_TYPE__ int_fast32_t;
typedef __INT_FAST64_TYPE__ int_fast64_t;

typedef signed int int_fast8_t;
typedef signed int int_fast16_t;
typedef signed int int_fast32_t;
typedef signed long long int_fast64_t;
typedef __INT_LEAST8_TYPE__ int_least8_t;
typedef __INT_LEAST16_TYPE__ int_least16_t;
typedef __INT_LEAST32_TYPE__ int_least32_t;
typedef __INT_LEAST64_TYPE__ int_least64_t;

typedef signed char int_least8_t;
typedef signed short int_least16_t;
typedef signed int int_least32_t;
typedef signed long long int_least64_t;
typedef __UINT8_TYPE__ uint8_t;
typedef __UINT16_TYPE__ uint16_t;
typedef __UINT32_TYPE__ uint32_t;
typedef __UINT64_TYPE__ uint64_t;

typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
typedef __UINT_FAST8_TYPE__ uint_fast8_t;
typedef __UINT_FAST16_TYPE__ uint_fast16_t;
typedef __UINT_FAST32_TYPE__ uint_fast32_t;
typedef __UINT_FAST64_TYPE__ uint_fast64_t;

typedef unsigned int uint_fast8_t;
typedef unsigned int uint_fast16_t;
typedef unsigned int uint_fast32_t;
typedef unsigned long long uint_fast64_t;
typedef __UINT_LEAST8_TYPE__ uint_least8_t;
typedef __UINT_LEAST16_TYPE__ uint_least16_t;
typedef __UINT_LEAST32_TYPE__ uint_least32_t;
typedef __UINT_LEAST64_TYPE__ uint_least64_t;

typedef unsigned char uint_least8_t;
typedef unsigned short uint_least16_t;
typedef unsigned int uint_least32_t;
typedef unsigned long long uint_least64_t;

typedef int intptr_t;
typedef unsigned int uintptr_t;
typedef __INTPTR_TYPE__ intptr_t;
typedef __UINTPTR_TYPE__ uintptr_t;

#ifdef __cplusplus
}

0 comments on commit f32330b

Please sign in to comment.
You can’t perform that action at this time.