Interrupt

The Best and Worst GCC Compiler Flags For Embedded | Interrupt

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 companion discussion topic for the original entry at https://interrupt.memfault.com/blog/best-and-worst-gcc-clang-compiler-flags

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.

1 Like

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.

1 Like

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;
      |      ^~~

I’ve tried many ways without succeeding.

how would you do it?

Best regards
Max

Unfortunately, I think your only option here is to cast.

uint8_t func(uint16_t val)
{
	uint8_t r;
	val += (uint16_t)0x7F;
	r = (uint8_t)(val >> 8);
	return r;
}

already tried:

#include <stdint.h>

uint8_t func(uint16_t val0, uint16_t val1, uint16_t val2)
{
	uint8_t r;
	val0 += 0x7F;
	val1 += (uint16_t)0x7F;
	val2 += 0x7FU;
	r = (uint8_t)((val0+val1+val2) >> 8);
	r <<= (uint8_t)(val2 & 0x3);
	return r;
}

Here are the errors I get:

$ 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
$