简体   繁体   中英

Compiling with different versions of gcc on ubuntu yields different results

So I have a project setup somthing like this:

  • myfile.cpp which includes:
    • fsl_clock.h

Where myfile is a c++ file and fsl_clock.h is a pure C header file from NXP where a version of it can be seen here

My file looks like:

#include "fsl_clock.h"

Now I do have more stuff in my file, but I emptied it until I was left with just that.

Here are the compilations I have tried along with results:

  • With the arm cross-compiler arm-none-eabi-g++ this compiles fine.
  • With host (x86Linux) g++ --version 7.3.0-16ubuntu3 it works fine
  • With host (x86Linux) g++ --version 7.3.0-27ubuntu1~18.04 it gives loads of strange errors.

The errors that I get are things like:

device/MIMX8MQ6_cm4.h8856:51: error 'reinterpret_cast<CMM_Type*>(808976384)' is not a constant expression

Where the line of code is pure C and looks like:

kCLOCK_RootM4 = (uint32_t)(&(CCM)->ROOT[1].TARGET_ROOT)

Where CCM is defined as:

#define CCM_BASE (0x30380000u)
#define CCM ((CCM_Type*)CCM_BASE)

So it looks like the newer g++ 7.3.0-27ubuntu1~18.04 (perhaps correctly) is doing c++ things (eg reinterpret_cast ) in the C-style included header code. The older compiler 7.3.0-16ubuntu3 is not behaving the same way - and the compile goes ok.

Can anyone say what the difference between the two compilers is and why one works and the other does not? Both compilers gnu g++ have the same g++ version 7.3.0. But I don't really understand the suffix 16ubuntu3 vs 27ubuntu1~18.04 and why that might change the behaviour...

Note Now, I know that for my host build I don't really want to be including board specific code on my host build, but that is a different matter. I am more interested at the moment in understanding why there is a difference between the two compilers.

Update

For the host build the compiler line looks like:

g++ -w -Isource/drivers -Isource/board -Isource/device -m32 -g -std=c++11 -c source/myfile.cpp -o out.o 

The CMM_Type (having to hand copy it as the original is buried in the NXP website) looks like (note its abbrieviated because there is too much to copy - but its structs of uint32_t's):

typedef struct {
   volatile uint32_t GPR0;
   volatile uint32_t GPR0_SET;
   struct {
      :
   } PLL_CTRL[39];
      :
   struct {
      volatile uint32_t TARGET_ROOT;
      volatile uint32_t TARGET_ROOT_SET;
      volatile uint32_t TARGET_ROOT_CLR;
         :
   } ROOT[142];
} CCM_Type;

Minimal Example - Online GDB I have made a minimal example - it does not compile with online GDB but it does produce the errors I explained on my compiler. Link is here

Minimal - Wandbox Exactly the same code as online GDB example, but this actually displays the same error I got: here

The minimal Example Code

#include <stdint.h>

typedef struct {
    struct {
        volatile uint32_t TARGET_ROOT;
    } ROOT[4];
} CCM_Type;

#define CCM_BASE (0x30380000u)
#define CCM ((CCM_Type *)CCM_BASE)

typedef enum _clock_root_control
{
    kCLOCK_RootM4 = (uint32_t)(&(CCM)->ROOT[1].TARGET_ROOT)
} clock_root_control_t;

int main()
{
    return 0;
}

reinterpret_cast and C style casts cannot be evaluated by the compiler at compile time, and this is particularly true when they create pointers that you then dereference. enum constants need to acquire values at compile time. What I would do in this situation is use the integer value CCM_BASE and offsetof .

#include <cstddef>

typedef enum _clock_root_control
{
    kCLOCK_RootM4 = CCM_BASE + offsetof(CCM_Type, ROOT[1].TARGET_ROOT)
} clock_root_control_t;

You know that all of your pointer dereferencing in your original example is pointless because you're just using the & operator to get an address. But, it doesn't matter. That dereferencing must still be valid and executable for the compiler to evaluate it at compile time. The address you're working with is meaningless to your compiler. Who knows what's there, or even if it refers to a mapped page? And certainly compiling a C++ program shouldn't result in random mucking about in the compiler's memory anyway.

And this is true for either C or C++. Since your code is technically undefined behavior, it may randomly work for some compilers. And most compilers that target platforms on which you'd want to actually use that sort of code will do what you expect when the code is executed at run time.

But, for compile time evaluation, the offsetof macro in <csstddef> handles all those kinds of details for you and is defined behavior. It's what you want and need here.

typedef enum _clock_root_control
{
    kCLOCK_RootM4 = (uint32_t)(&(CCM)->ROOT[1].TARGET_ROOT)
} clock_root_control_t;

This code isn't valid in either C or C++.

In C++, an enumerator must be a constant expression. A constant expression cannot involve reinterpret_cast . A C-style cast from pointer to integer is equivalent to reinterpret_cast .

In C, an enumerator must be an integer constant expression. An integer constant expression cannot involve pointer operands.

A way to fix this is to replace the expression with an equivalent constant expression, eg

CCM_BASE + offsetof(CCM_Type, ROOT[1].TARGET_ROOT)

Live example

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM