Skip to content

stdint.h: streamline type definitions #16645

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 26, 2019
Merged

Conversation

npitre
Copy link
Collaborator

@npitre npitre commented Jun 5, 2019

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.

Copy link
Collaborator

@andyross andyross left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh, as soon as I saw this PR I went to see if it was failing on newlib, and of course it is. :)

I approve in principle, but note that the newlib "support" in gcc is an infuriating mess. Lots of stuff you expect would be invariant with the toolchain turns out to be sensitive to the C library, and in particular newlib wants the int32_t type to be "long" and not "int" on 32 bit platforms, even though you'd think that wouldn't make any difference. Or something. I forget the details but remember them being painful.

@npitre
Copy link
Collaborator Author

npitre commented Jun 5, 2019 via email

@aescolar aescolar added area: ARM ARM (32-bit) Architecture area: C Library C Standard Library area: Kernel labels Jun 6, 2019
Copy link
Contributor

@andrewboie andrewboie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In principle I have nothing against this patch and it seems like a good change...but need to resolve newlib issues

@@ -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",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this change is breaking builds with newlib enabled

@npitre
Copy link
Collaborator Author

npitre commented Jun 6, 2019 via email

@andrewboie
Copy link
Contributor

click on the details for the failed shippable run, you will see logs for failing test cases

@npitre
Copy link
Collaborator Author

npitre commented Jun 6, 2019 via email

@andrewboie
Copy link
Contributor

If you run sanitycheck locally you should see the failures.

The tests failing are:

tests/net/socket/getnameinfo/net.socket
tests/net/socket/tcp/net.socket.tcp
tests/lib/mem_alloc/libraries.libc.newlib
tests/net/socket/misc/net.socket
tests/net/socket/select/net.socket

on mps2_an385 target.

@npitre
Copy link
Collaborator Author

npitre commented Jun 6, 2019 via email

@andrewboie
Copy link
Contributor

andrewboie commented Jun 6, 2019

Builds just fine for me.

Example.

cd tests/lib/mem_alloc
mkdir out
cd out
cmake -DCONF_FILE=prj_newlib.conf -DBOARD=mps2_an385 ..
make -j8

@npitre
Copy link
Collaborator Author

npitre commented Jun 6, 2019 via email

@andyross
Copy link
Collaborator

andyross commented Jun 6, 2019

Toolchain version maybe? Again, the root of the "integer types vs. newlib" problem is that the gcc toolchain headers will end up detecting newlib very early and exposing different types via what you would think are gcc-only preprocessor symbols. Maybe this finally got addressed upstream?

@npitre
Copy link
Collaborator Author

npitre commented Jun 6, 2019 via email

@npitre
Copy link
Collaborator Author

npitre commented Jun 6, 2019 via email

CMakeLists.txt Outdated
-U__UINT32_TYPE__ "SHELL:-D__UINT32_TYPE__='unsigned int'"
-U__INTPTR_TYPE__ "SHELL:-D__INTPTR_TYPE__='long int'"
-U__UINTPTR_TYPE__ "SHELL:-D__UINTPTR_TYPE__='long unsigned int'"
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about using the gcc option

-imacros enforce_standard_type_correspondance.h

instead here?

This would shorten the command line and allow us to use

#undef

and

#define

which readers will be more familiar with.

Also allows us to avoid using SHELL, which I am not familiar with, and add_compile_options, which causes problems for the zephyr_get_* API.

imacro should generally be avoided, as symbols should not magically appear in the source, but symbols that are prefixed with __ shouldn't be expected to be in-source anyway.

@npitre
Copy link
Collaborator Author

npitre commented Jun 12, 2019 via email

@zephyrbot zephyrbot added the area: API Changes to public APIs label Jun 12, 2019
@npitre
Copy link
Collaborator Author

npitre commented Jun 12, 2019 via email

@npitre npitre force-pushed the stdint.h branch 2 times, most recently from 3045b01 to 180241b Compare June 13, 2019 01:15
CMakeLists.txt Outdated
@@ -208,6 +208,11 @@ zephyr_compile_options(
${TOOLCHAIN_C_FLAGS}
)

# @Intent: Enforce standard type correspondance to match Zephyr usage.
zephyr_compile_options(
--imacros=${ZEPHYR_BASE}/include/toolchain/zephyr_stdint.h
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was not aware --flag=value was supported for imacros. This simplifies things.

Nice find.

CMakeLists.txt Outdated
@@ -208,6 +208,11 @@ zephyr_compile_options(
${TOOLCHAIN_C_FLAGS}
)

# @Intent: Enforce standard type correspondance to match Zephyr usage.
zephyr_compile_options(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, but now that this commit: "4ddbc0096a5e9925a27ee3a0b130fd78373650b6"

is merged, we must, for toolchain portability reasons, use

toolchain_cc_imacros

instead of

zephyr_compile_options

and patch this line:

4ddbc00#diff-6c754805e58aa2a9bce0dbdf0414d7f5R7

to use the format that does not have whitespace, meaning "--imacros=foo" instead of "-imacros foo".

Nicolas Pitre added 2 commits June 13, 2019 14:23
Because CMake explicitly deduplicates arguments, it is not possible
to use toolchain_cc_imacros() multiple times as the later "-imacros"
are stripped away, leaving the associated file arguments dangling.
The documented workaround in the CMake manual involves some "SHELL:..."
construct but that doesn't get through zephyr_compile_options()
undammaged.

Let's simply remove this issue altogether by replacing "-imacros x.h"
with the joined form "--imacros=x.h" instead. Both gcc and clang
support this syntax.

FYI, this joined form is also available for other arguments such as:

	-include x.h   -->   --include=x.h
	-A foo         -->   --assert=foo
	-D foo         -->   --define-macro=foo
	-U foo         -->   --undefine-macro=foo

Etc.

Signed-off-by: Nicolas Pitre <[email protected]>
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 <[email protected]>
@andrewboie
Copy link
Contributor

@SebastianBoe can you re-visit your review?

@ghost
Copy link

ghost commented Jun 25, 2019

I am uncomfortable with this patch because it relies on compiler-specific features on two levels: first, it relies on the existence of INT16_TYPE et al., and second, it relies on a non-standard feature of the preprocessor (-imacros). This seems to be at odds with the works that's been underway for quite a while to make the build process and the codebase itself toolchain-independent.

Is there a problem we're trying to solve? Is the medicine worse than the cure? Am I misunderstanding something?

@npitre
Copy link
Collaborator Author

npitre commented Jun 25, 2019 via email

@ghost
Copy link

ghost commented Jun 25, 2019

And -imacros may be replaced by -include just as well (it only needs to be listed first and contain nothing else than #define's). This isn't the first use of imacros either.

-include is just as build-system specific as -imacros. Neither is a standard feature. But, fair enough, these aren't new occurrences so they'll both have to be dealt with anyway.

That will save our sanity in the long run.

Oh, good. I have such little sanity left. :-)

@andrewboie
Copy link
Contributor

@SebastianBoe ping

Copy link
Collaborator

@pfalcon pfalcon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@npitre: Thanks for tackling this issue. There's a bit of heritage in Zephyr with this stuff. The matter, either you embrace all the goodies the contemporary C standards bring. And besides nice things like uint32_t and uintptr_t, that also includes more "cumbersome" things like "foo" PRIi8 "bar". If you don't embrace all those things, the only way is to reinvent your own wheel, and that's how Zephyr got its own types like u8_t (yeah, I guess the motivation also was "if Linux does it, why can't we").

Unfortunately, that reinventing is not one-time thing, it keeps biting going forward, disallowing to use nice things like intptr_t. And it doesn't stop at that level either, because we have further conflicts on the POSIX subsys vs Newlib, etc. level. Bottom line: I have to admit I'm a bit concerned to hear about newlib issues mentioned with this patch, but I think the only way to deal with all those issues is to resolve them, step by step, instead of avoid using workarounds, more and more. In that regard, this patch looks like going in the right direction, and if it passes CI, +1 should be just the right choice for it. Thanks.

@nashif nashif merged commit f32330b into zephyrproject-rtos:master Jun 26, 2019
@npitre npitre deleted the stdint.h branch July 19, 2019 15:47
mnkp added a commit to mnkp/zephyr that referenced this pull request Oct 18, 2019
PR zephyrproject-rtos#16645 streamlined stdint.h type definitions. This has an unfortunate
side effect that any legacy code which assumed default, compiler
provided types will now generate warnings any time type mismatch is
detected during the compilation process. Redefining stdint types also
significantly increases porting effort when building an existing
bare metal application with Zephyr.

This PR makes redefinition of stdint types optional.

Signed-off-by: Piotr Mienkowski <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: API Changes to public APIs area: ARM ARM (32-bit) Architecture area: Build System area: C Library C Standard Library area: Kernel
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants