Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to bypass gcc's -Wconversion

Tags:

c

gcc

int main() {

    struct { unsigned int a:20; } s;
    unsigned int val = 0xaabbc000;

    s.a = val & 0xfffff;         // 1) works
    s.a = (val >> 12) & 0xfffff; // 2) generates -Wconversion warning
}

I'm compiling a project with -Wconversion and I hit a case where I'm unable to convince the compiler that I'm fine with the conversion.

  • In case 1, I'm using the same solution proposed in c++ bit fields and -Wconversion and it works great. It forces the compiler to accept the conversion because of the bitmask which limits value's range.

  • In case 2, however, because of the shift (but why?) the compiler refuses to accept the conversion. And complains in the following way:

    $ gcc wconv.c -Wconversion -Werror
    wconv.c: In function ‘main’:
    wconv.c:8:11: error: conversion to ‘unsigned int:20’ from ‘unsigned int’ may alter its value [-Werror=conversion]
     s.a = (val >> 12) & 0xfffff; // 2) generates -Wconversion warning
           ^
    cc1: all warnings being treated as errors
    

    (Interesting note: with clang the code compiles without issues. I've observed so far that clang's -Wconversion is much less stricter than GCC's.)

Questions:

  • How can I convince GCC to compile case 2?
  • But also, why does that right shift change everything? In my understanding, given an expression having unsigned int type, a bit shift operation should not alter its type.
  • And finally, could this be possibly a compiler bug?

Note[1]: this question is not a duplicate of: c++ bit fields and -Wconversion because the solutions proposed there simply do not work in my case.

Note[2]: this question is not a duplicate of: Why >>24 causes -Wconversion but >>23 doesn't? because refers to a different bug (or different manifestation of the same core-bug) and has a simple workaround using a cast, like proposed in c++ bit fields and -Wconversion, at least with GCC 7.3.

like image 726
vvaltchev Avatar asked Jan 29 '19 13:01

vvaltchev


1 Answers

I just discovered that in the GCC's bug tracker there are several bugs related with -Wconversion. In particular: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39170

Specifically, comment #18 (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39170#c18) reports an example almost identical to mine:

#include <stdint.h>

struct foo
{
   unsigned bar: 30;
   unsigned fill: 2;
};

struct foo test(uint32_t value)
{
   struct foo foo;

   foo.bar = (value >> 2) & 0x3fffffffU;

   return foo;
}

Therefore, I believe that this issue is definitively a gcc bug.

Personal workaround

Given the compiler's bug, my personal workaround was to just wrap the right shift operation in a static always_inline function, even if I'm not particularly happy by this hack.

#include <stdint.h>

static __attribute__((always_inline)) inline uintptr_t
rshift(uintptr_t val, uintptr_t bits)
{
   return val >> bits;
}

int main() {

    struct { unsigned int a:20; } s;
    unsigned int val = 0xaabbc000;

    s.a = val & 0xfffff;                // 1) works
    s.a = (rshift(val, 12)) & 0xfffff;  // 2) works
}

Workaround suggested by PSkocik

   s.a = (unsigned){(val >> 12)} & 0xfffff; // works

Which is my favorite by now.

like image 113
vvaltchev Avatar answered Oct 16 '22 03:10

vvaltchev