0

I am having issues with optimization in an embedded C application. The address of the pointer changes to 0x0 unexpectedly and breaks my code.

I am using GNU MCU Eclipse ARM Embedded GCC toolchain. I am using a sensor with which I with communicate via SPI. The sensor has 8 configuration registers of 16bits each. I keep a local copy of the configurations in a struct called “hallsensor_zTleConfig” I read the sensor’s stored configuration regularly (“zTleSensorConfig”) and compare it against the local copy to see if any memory corruption occurred.

With no optimization this process works.

With optimization, in the code below, the pointer to the master/local copy suddenly changes to 0x0 for no apparent reason.

// Master copy of configuration of the TLE chips
static TLE5012B_zCONFIG hallsensor_zTleConfig[ HALLSENSOR_COUNT ];

static void Hallsensor_VerifyConfig( HALLSENSOR_eUNIT eUnit )
{
    TLE5012B_zCONFIG zTleSensorConfig;
    uint16* pu16MasterConfigPointer = &hallsensor_zTleConfig[ eUnit ];
    uint16* pu16SensorConfigPointer = &zTleSensorConfig;
    uint8 u8Index;

    // Read all configuration parameters
    (void)Hallsensor_ReadRegister( TLE5012B_MOD_2, &zTleSensorConfig, sizeof( TLE5012B_zCONFIG ) );

    // Copy over configurations modified by Auto-calibration and reserved bits
    hallsensor_zTleConfig[ eUnit ].zMod3.u12ANG_BASE     =     zTleSensorConfig.zMod3.u12ANG_BASE;
    hallsensor_zTleConfig[ eUnit ].zSynch.i12AmpSynch    =     zTleSensorConfig.zSynch.i12AmpSynch;
    hallsensor_zTleConfig[ eUnit ].zOffsetX.i12XOffset   = zTleSensorConfig.zOffsetX.i12XOffset;
    hallsensor_zTleConfig[ eUnit ].zOffsetY.i12YOffset   = zTleSensorConfig.zOffsetY.i12YOffset;
    hallsensor_zTleConfig[ eUnit ].zMod4.u7TCO_X_T       = zTleSensorConfig.zMod4.u7TCO_X_T;
    hallsensor_zTleConfig[ eUnit ].zTcoY.u7TCO_Y_T       = zTleSensorConfig.zTcoY.u7TCO_Y_T;
    hallsensor_zTleConfig[ eUnit ].zMod2.u1Reserved      = zTleSensorConfig.zMod2.u1Reserved;
    hallsensor_zTleConfig[ eUnit ].zMod4.u1Reserved      = zTleSensorConfig.zMod4.u1Reserved;
    hallsensor_zTleConfig[ eUnit ].zOffsetX.u4Reserved   = zTleSensorConfig.zOffsetX.u4Reserved;
    hallsensor_zTleConfig[ eUnit ].zOffsetY.u4Reserved   = zTleSensorConfig.zOffsetY.u4Reserved;
    hallsensor_zTleConfig[ eUnit ].zSynch.u4Reserved     = zTleSensorConfig.zSynch.u4Reserved;

    // Compare local Master config setting to sensor settings and look for corrupted data
    for ( u8Index = 0; u8Index < TLE5012B_CONFIG_REG_COUNT; u8Index++ )
    {
        // Configuration invalid?
        if ( pu16MasterConfigPointer[u8Index] != pu16SensorConfigPointer[u8Index] )
        {
            hallsensor_bConfigOk[ eUnit ] = FALSE;
        }
    }

    // Data corrupted?
    if ( !hallsensor_bConfigOk[ eUnit ] )
    {
        // Do stuff
    }
}

At the start of the function, pu16MasterConfigPointer and pu16SensorConfigPointer correctly hold the address of their corresponding structs. However, right before the first for loop, pu16MasterConfigPointer changes to 0x0 and all the for loop conditions result TRUE.

I have tried many combinations of volatile for different variables and pointers and the results still break the code in various other ways (e.g. for loop does not branch out for if statement looking at the asm code resulting in statement always going to "hallsensor_bConfigOk[ eUnit ] = FALSE;")

PS: A good resource to learn about how to protect your code against optimization issues would be very welcome.

Screenshot of debugger showing pointer = 0x0 w/ asm code

enter image description here

Alain Merigot
  • 10,667
  • 3
  • 18
  • 31
NoyanAbi
  • 11
  • 2
  • Not the answer, but the inline assembly listing looks neat. What IDE is that? – HolyBlackCat Jan 30 '19 at 23:25
  • `TLE5012B_zCONFIG zTleSensorConfig;` appears to shadow a global variable. It is also uninitialised at the time of first reference. – wildplasser Jan 30 '19 at 23:28
  • Oh, it appears I was confused by your long variable names. Local variable names don't need to be that long, since they are local to the function. – wildplasser Jan 30 '19 at 23:33
  • 1
    Are you certain that `Hallsensor_ReadRegister` is not writing more than `sizeof(TLE5012B_zCONFIG)` bytes? If it is, that might be where your pointer is getting clobbered. – Mike Holt Jan 30 '19 at 23:35
  • 1
    Also, stucts are assignable lvalues. – wildplasser Jan 30 '19 at 23:38
  • `uint16* pu16MasterConfigPointer = &hallsensor_zTleConfig[ eUnit ];` is an error (unless `TLE5012B_zCONFIG` and `uint16` are typedefs for the same type, and `TLE5012B_CONFIG_REG_COUNT == 1`). – M.M Jan 30 '19 at 23:53
  • 1
    It would help to show the definition of `TLE5012B_zCONFIG` . (It would help even more to post a MCVE) – M.M Jan 30 '19 at 23:54
  • 1
    I guess - UB in the code. If the optimization does such a things it means - the error is somewhere in the code. It has just been unveiled. In similar cases you should look for the error in your code as long as it is needed to find it. BTW - as higher optimisation as more dificult is to see what code is generated for the particular line. – 0___________ Jan 31 '19 at 00:07
  • 1
    Not only are the initializations of `pu16MasterConfigPointer` and `pu16SensorConfigPointer` invalid as given, but also, accessing the pointed-to objects through those pointers violates the strict aliasing rule, producing undefined behavior. It would be OK if instead they were pointers to a character type, such as (probably) `uint8_t`. Or do you have `memcmp()` available to you? Would that be a viable alternative for comparing the structures? – John Bollinger Jan 31 '19 at 03:51
  • you should take care about undefined behavior instead of wondering *how to protect your code against optimization issues*. And asking for a book or tutorial is off-topic here – phuclv Jan 31 '19 at 06:18
  • 1
    @HolyBlackCat, the image is from the debugger Ozone. – NoyanAbi Jan 31 '19 at 16:58

1 Answers1

0

@John Bollinger was right saying that the pointers violate the strict aliasing rule which was unknown to me. There is a good explanation of this rule in this post.

Replacing the for loop with memcmp did the trick.

NoyanAbi
  • 11
  • 2