Compilers have hundreds of flags and configuration settings which can be toggled to control
performance optimizations, code size, error checks and diagnostic information emitted. Often
these settings wind up being copy and pasted from project to project, makefile to makefile, but
it’s good to periodically audit the current options selected for a project.
This is a gold mine for young developers like me (same as the other articles), basically saves us days or months of sailing through documentation. just wanted to thank you for the hard work.
Great post! Regarding struct packing, another common use case in embedded is when defining a struct to overlay memory for a peripheral (or similar). If the compiler adds padding to the struct definition, the memory overlay will be incorrect when using it to access the underlying peripheral registers. This can lead to all sorts of problems at runtime that can be tough to track down.
I have a piece of fake code (I need it to generate the errors) that I don’t know how to refactor to avoid errors.
#include <stdint.h>
uint8_t func(uint16_t val)
{
uint8_t r;
val += 0x7F;
r = val >> 8;
return r;
}
The error is the same you get:
$ gcc -Wall -Wextra -Wconversion -O0 module.c -c -o module.o
module.c: In function 'func':
module.c:6:9: warning: conversion from 'int' to 'uint16_t' {aka 'short unsigned int'} may change value [-Wconversion]
6 | val += 0x7F;
| ^~~~
module.c:7:6: warning: conversion from 'int' to 'uint8_t' {aka 'unsigned char'} may change value [-Wconversion]
7 | r = val >> 8;
| ^~~
$ gcc -Wall -Wextra -Wconversion -O0 module.c -c -o module.o
module.c: In function 'func':
module.c:6:10: warning: conversion from 'int' to 'uint16_t' {aka 'short unsigned int'} may change value [-Wconversion]
6 | val0 += 0x7F;
| ^~~~
module.c:7:10: warning: conversion from 'int' to 'uint16_t' {aka 'short unsigned int'} may change value [-Wconversion]
7 | val1 += (uint16_t)0x7F;
| ^
module.c:8:10: warning: conversion from 'unsigned int' to 'uint16_t' {aka 'short unsigned int'} may change value [-Wconversion]
8 | val2 += 0x7FU;
| ^~~~~
module.c:10:8: warning: conversion from 'int' to 'uint8_t' {aka 'unsigned char'} may change value [-Wconversion]
10 | r <<= (uint8_t)(val2 & 0x3);
| ^
In this post it is said that if I use -Wconversion I can no longer use compound assignment operator. Is that true?
I understand that they advise against the use of -Wconversion because it brings more problems than benefits.
In an averagely large project I guess that the false positives are many compared to the real bugs in the code base.
I misread the error, it’s the += that it’s objecting to.
Here’s the fixed version:
uint8_t func(uint16_t val)
{
val = (uint16_t)(val + 0x7F);
uint8_t r = (uint8_t)(val >> 8);
return r;
}
compiled it with:
$ gcc-9 --version
gcc-9 (Homebrew GCC 9.2.0_2) 9.2.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ gcc-9 -Wall -Wextra -Wconversion -O0 test.c
$
Hi, Fantastic post!!!
I adopted a lot of the flags pointed to by this post for my project. I have several bit fields in structures. I am running into issues with bit fileds and -Werror=conversion flag. An example is shown below:
The compile error points to the opt->_led_control.ICL_DRV = current; statement. error: conversion from 'uint8_t' {aka 'unsigned char'} to 'unsigned char:2' may change value [-Werror=conversion]
The only way to get past these errors (I use -Werror flag too) is to use bit masks and shifts, but that completely defeats the purpose of the bit field. Any thoughts on how to work around this?
Hello!
Thanks for interesting article. I have added most of the flags for the current embedded project I am currently working. But I have hit a snag when adding the flag -Wdouble-promotion.
Since printf() seems to not have a formatting flag for float, only double, I get warnings (aka errors) when trying to print out a float. It is all defined in the attribute __format__, at least for GCC. Not easy to go around it with any tricks.
Very nice article, thanks!
Just a small hint on -Wdouble-promotion for sake of correctness.
Some Cortex-M7 have double precision FPU, like the NXP i.MX RT 1064 I’m working with.
Cheers.
Alex.
Some flags that should also be mentioned together with -ffunction-sections and -fdata-sections are their counterparts -mpragma-function-sections and -mpragma-data-sections: When using #pragma section for explicit memory mapping, having the data or function postfix is still very useful, and the -ffunction-sections and -fdata-sections do not apply to sections that are declared using a #pragma. I’ve stumbled upon this in a project where each and every variable/constant/text is placed explicitly.
We came to a similar conclusions over almost all of mentioned flags.
And here’s the current set of warnings we treat as errors.
No code with any of such warnings is safe enough to even start testing.
Until you’re ready to swith to -Werror, this set is a great place to be.