EmbeddedRelated.com
Forums
The 2024 Embedded Online Conference

gnu compiler optimizes out "asm" statements

Started by Tim Wescott May 28, 2015
This is related to my question about interrupts in an STM32F303 
processor.  It turns out that the problem is in the compiler (or I'm 
going insane, which is never outside the realm of possibility when I'm 
working on embedded software).

I'm coding in C++, and I'm using a clever dodge for protecting chunks of 
code from getting interrupted.  Basically, I have a class that protects a 
block of code from being interrupted.  The constructor saves the 
interrupt state then disables interrupts, and the destructor restores 
interrupts.

This has been reliable for me for years, but now the destructor is not 
being called.  I suspect that the optimizer can't make sense of it 
because of the asm statements, and is throwing it away.

If someone knows the proper gnu-magic to tell the optimizer not to do 
that, I'd appreciate it.  I'm going to look in my documentation, but I 
want to make sure I use the right method, and don't just stumble onto 
something that works for now but should be depreciated, or is fragile, or 
whatever.

Here's the "protect a block" class:

typedef class CProtect
{
  public:

  CProtect(void)
  {
    int primask_copy;
    asm("mrs %[primask_copy], primask\n\t"     // save interrupt status
        "cpsid i\n\t"             // disable interrupts
        : [primask_copy] "=r" (primask_copy));
    _primask = primask_copy;
  }

  ~CProtect()
  {
    int primask_copy = _primask;
    // Restore interrupts to their previous value
    asm("msr primask, %[primask_copy]" : : [primask_copy]
        "r" (primask_copy));
  }

  private:
  volatile int  _primask;
} CProtect;

and here's how it's used:

        {
          CProtect protect;

          // critical code goes here
        }

-- 

Tim Wescott
Wescott Design Services
http://www.wescottdesign.com
Le 28/05/2015 20:27, Tim Wescott a écrit :
> This is related to my question about interrupts in an STM32F303 > processor. It turns out that the problem is in the compiler (or I'm > going insane, which is never outside the realm of possibility when I'm > working on embedded software). > > I'm coding in C++, and I'm using a clever dodge for protecting chunks of > code from getting interrupted. Basically, I have a class that protects a > block of code from being interrupted. The constructor saves the > interrupt state then disables interrupts, and the destructor restores > interrupts. > > This has been reliable for me for years, but now the destructor is not > being called. I suspect that the optimizer can't make sense of it > because of the asm statements, and is throwing it away. > > If someone knows the proper gnu-magic to tell the optimizer not to do > that, I'd appreciate it. I'm going to look in my documentation, but I > want to make sure I use the right method, and don't just stumble onto > something that works for now but should be depreciated, or is fragile, or > whatever. > > Here's the "protect a block" class: > > typedef class CProtect > { > public: > > CProtect(void) > { > int primask_copy; > asm("mrs %[primask_copy], primask\n\t" // save interrupt status > "cpsid i\n\t" // disable interrupts > : [primask_copy] "=r" (primask_copy)); > _primask = primask_copy; > } > > ~CProtect() > { > int primask_copy = _primask; > // Restore interrupts to their previous value > asm("msr primask, %[primask_copy]" : : [primask_copy] > "r" (primask_copy)); > } > > private: > volatile int _primask; > } CProtect; > > and here's how it's used: > > { > CProtect protect; > > // critical code goes here > } >
Are you sure that the constructor is called? If the variable protect is not used, it could be "optimized away"
On 2015-05-28, Tim Wescott <seemywebsite@myfooter.really> wrote:
> > I'm coding in C++, and I'm using a clever dodge for protecting chunks of > code from getting interrupted. Basically, I have a class that protects a > block of code from being interrupted. The constructor saves the > interrupt state then disables interrupts, and the destructor restores > interrupts. > > This has been reliable for me for years, but now the destructor is not > being called. I suspect that the optimizer can't make sense of it > because of the asm statements, and is throwing it away. >
Are you sure the destructor is not being called or is it just the asm statement in the destructor which is being optimised away ?
> If someone knows the proper gnu-magic to tell the optimizer not to do > that, I'd appreciate it. I'm going to look in my documentation, but I > want to make sure I use the right method, and don't just stumble onto > something that works for now but should be depreciated, or is fragile, or > whatever. >
Try marking the asm statement itself as volatile. See https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html for some discussion. Simon. -- Simon Clubley, clubley@remove_me.eisner.decus.org-Earth.UFP Microsoft: Bringing you 1980s technology to a 21st century world
On Thu, 28 May 2015 20:32:48 +0200, Lanarcam wrote:

> Le 28/05/2015 20:27, Tim Wescott a &eacute;crit : >> This is related to my question about interrupts in an STM32F303 >> processor. It turns out that the problem is in the compiler (or I'm >> going insane, which is never outside the realm of possibility when I'm >> working on embedded software). >> >> I'm coding in C++, and I'm using a clever dodge for protecting chunks >> of code from getting interrupted. Basically, I have a class that >> protects a block of code from being interrupted. The constructor saves >> the interrupt state then disables interrupts, and the destructor >> restores interrupts. >> >> This has been reliable for me for years, but now the destructor is not >> being called. I suspect that the optimizer can't make sense of it >> because of the asm statements, and is throwing it away. >> >> If someone knows the proper gnu-magic to tell the optimizer not to do >> that, I'd appreciate it. I'm going to look in my documentation, but I >> want to make sure I use the right method, and don't just stumble onto >> something that works for now but should be depreciated, or is fragile, >> or whatever. >> >> Here's the "protect a block" class: >> >> typedef class CProtect { >> public: >> >> CProtect(void) >> { >> int primask_copy; >> asm("mrs %[primask_copy], primask\n\t" // save interrupt >> status >> "cpsid i\n\t" // disable interrupts : >> [primask_copy] "=r" (primask_copy)); >> _primask = primask_copy; >> } >> >> ~CProtect() >> { >> int primask_copy = _primask; >> // Restore interrupts to their previous value asm("msr primask, >> %[primask_copy]" : : [primask_copy] >> "r" (primask_copy)); >> } >> >> private: >> volatile int _primask; >> } CProtect; >> >> and here's how it's used: >> >> { >> CProtect protect; >> >> // critical code goes here >> } >> > Are you sure that the constructor is called? > If the variable protect is not used, it could be "optimized away"
Yes -- the constructor is what disables interrupts. -- Tim Wescott Wescott Design Services http://www.wescottdesign.com
On Thu, 28 May 2015 13:27:31 -0500, Tim Wescott wrote:

> This is related to my question about interrupts in an STM32F303 > processor. It turns out that the problem is in the compiler (or I'm > going insane, which is never outside the realm of possibility when I'm > working on embedded software). > > I'm coding in C++, and I'm using a clever dodge for protecting chunks of > code from getting interrupted. Basically, I have a class that protects > a block of code from being interrupted. The constructor saves the > interrupt state then disables interrupts, and the destructor restores > interrupts. > > This has been reliable for me for years, but now the destructor is not > being called. I suspect that the optimizer can't make sense of it > because of the asm statements, and is throwing it away. > > If someone knows the proper gnu-magic to tell the optimizer not to do > that, I'd appreciate it. I'm going to look in my documentation, but I > want to make sure I use the right method, and don't just stumble onto > something that works for now but should be depreciated, or is fragile, > or whatever. > > Here's the "protect a block" class: > > typedef class CProtect { > public: > > CProtect(void) > { > int primask_copy; > asm("mrs %[primask_copy], primask\n\t" // save interrupt status > "cpsid i\n\t" // disable interrupts : [primask_copy] > "=r" (primask_copy)); > _primask = primask_copy; > } > > ~CProtect() > { > int primask_copy = _primask; > // Restore interrupts to their previous value asm("msr primask, > %[primask_copy]" : : [primask_copy] > "r" (primask_copy)); > } > > private: > volatile int _primask; > } CProtect; > > and here's how it's used: > > { > CProtect protect; > > // critical code goes here > }
Another data point: I'm optimizing at O1. When I build at O0, it works. -- Tim Wescott Wescott Design Services http://www.wescottdesign.com
On 2015-05-28, Tim Wescott <seemywebsite@myfooter.really> wrote:
> > Another data point: I'm optimizing at O1. When I build at O0, it works. >
In that case, try my suggestion of marking the asm statement itself as volatile. Simon. -- Simon Clubley, clubley@remove_me.eisner.decus.org-Earth.UFP Microsoft: Bringing you 1980s technology to a 21st century world
Le 28/05/2015 21:04, Tim Wescott a &eacute;crit :
> On Thu, 28 May 2015 13:27:31 -0500, Tim Wescott wrote: > >> This is related to my question about interrupts in an STM32F303 >> processor. It turns out that the problem is in the compiler (or I'm >> going insane, which is never outside the realm of possibility when I'm >> working on embedded software). >> >> I'm coding in C++, and I'm using a clever dodge for protecting chunks of >> code from getting interrupted. Basically, I have a class that protects >> a block of code from being interrupted. The constructor saves the >> interrupt state then disables interrupts, and the destructor restores >> interrupts. >> >> This has been reliable for me for years, but now the destructor is not >> being called. I suspect that the optimizer can't make sense of it >> because of the asm statements, and is throwing it away. >> >> If someone knows the proper gnu-magic to tell the optimizer not to do >> that, I'd appreciate it. I'm going to look in my documentation, but I >> want to make sure I use the right method, and don't just stumble onto >> something that works for now but should be depreciated, or is fragile, >> or whatever. >> >> Here's the "protect a block" class: >> >> typedef class CProtect { >> public: >> >> CProtect(void) >> { >> int primask_copy; >> asm("mrs %[primask_copy], primask\n\t" // save interrupt status >> "cpsid i\n\t" // disable interrupts : [primask_copy] >> "=r" (primask_copy)); >> _primask = primask_copy; >> } >> >> ~CProtect() >> { >> int primask_copy = _primask; >> // Restore interrupts to their previous value asm("msr primask, >> %[primask_copy]" : : [primask_copy] >> "r" (primask_copy)); >> } >> >> private: >> volatile int _primask; >> } CProtect; >> >> and here's how it's used: >> >> { >> CProtect protect; >> >> // critical code goes here >> } > > Another data point: I'm optimizing at O1. When I build at O0, it works. >
Could you try: Cprotect *protect = new Cprotect();
On Thu, 28 May 2015 19:06:04 +0000, Simon Clubley wrote:

> On 2015-05-28, Tim Wescott <seemywebsite@myfooter.really> wrote: >> >> Another data point: I'm optimizing at O1. When I build at O0, it >> works. >> >> > In that case, try my suggestion of marking the asm statement itself as > volatile. > > Simon.
The compiler doesn't allow that. -- Tim Wescott Wescott Design Services http://www.wescottdesign.com
On Thu, 28 May 2015 21:10:08 +0200, Lanarcam wrote:

> Le 28/05/2015 21:04, Tim Wescott a &eacute;crit : >> On Thu, 28 May 2015 13:27:31 -0500, Tim Wescott wrote: >> >>> This is related to my question about interrupts in an STM32F303 >>> processor. It turns out that the problem is in the compiler (or I'm >>> going insane, which is never outside the realm of possibility when I'm >>> working on embedded software). >>> >>> I'm coding in C++, and I'm using a clever dodge for protecting chunks >>> of code from getting interrupted. Basically, I have a class that >>> protects a block of code from being interrupted. The constructor >>> saves the interrupt state then disables interrupts, and the destructor >>> restores interrupts. >>> >>> This has been reliable for me for years, but now the destructor is not >>> being called. I suspect that the optimizer can't make sense of it >>> because of the asm statements, and is throwing it away. >>> >>> If someone knows the proper gnu-magic to tell the optimizer not to do >>> that, I'd appreciate it. I'm going to look in my documentation, but I >>> want to make sure I use the right method, and don't just stumble onto >>> something that works for now but should be depreciated, or is fragile, >>> or whatever. >>> >>> Here's the "protect a block" class: >>> >>> typedef class CProtect { >>> public: >>> >>> CProtect(void) >>> { >>> int primask_copy; >>> asm("mrs %[primask_copy], primask\n\t" // save interrupt >>> status >>> "cpsid i\n\t" // disable interrupts : >>> [primask_copy] >>> "=r" (primask_copy)); >>> _primask = primask_copy; >>> } >>> >>> ~CProtect() >>> { >>> int primask_copy = _primask; >>> // Restore interrupts to their previous value asm("msr primask, >>> %[primask_copy]" : : [primask_copy] >>> "r" (primask_copy)); >>> } >>> >>> private: >>> volatile int _primask; >>> } CProtect; >>> >>> and here's how it's used: >>> >>> { >>> CProtect protect; >>> >>> // critical code goes here >>> } >> >> Another data point: I'm optimizing at O1. When I build at O0, it >> works. >> > Could you try: Cprotect *protect = new Cprotect();
I could, but here at Wescott Design Services we have a fairly hard to overcome rule that says "don't thrash the heap". My boss would kill me, which would hurt me twice because I'm my boss. -- Tim Wescott Wescott Design Services http://www.wescottdesign.com
On Thu, 28 May 2015 13:27:31 -0500, Tim Wescott wrote:

> This is related to my question about interrupts in an STM32F303 > processor. It turns out that the problem is in the compiler (or I'm > going insane, which is never outside the realm of possibility when I'm > working on embedded software). > > I'm coding in C++, and I'm using a clever dodge for protecting chunks of > code from getting interrupted. Basically, I have a class that protects > a block of code from being interrupted. The constructor saves the > interrupt state then disables interrupts, and the destructor restores > interrupts. > > This has been reliable for me for years, but now the destructor is not > being called. I suspect that the optimizer can't make sense of it > because of the asm statements, and is throwing it away. > > If someone knows the proper gnu-magic to tell the optimizer not to do > that, I'd appreciate it. I'm going to look in my documentation, but I > want to make sure I use the right method, and don't just stumble onto > something that works for now but should be depreciated, or is fragile, > or whatever. > > Here's the "protect a block" class: > > typedef class CProtect { > public: > > CProtect(void) > { > int primask_copy; > asm("mrs %[primask_copy], primask\n\t" // save interrupt status > "cpsid i\n\t" // disable interrupts : [primask_copy] > "=r" (primask_copy)); > _primask = primask_copy; > } > > ~CProtect() > { > int primask_copy = _primask; > // Restore interrupts to their previous value asm("msr primask, > %[primask_copy]" : : [primask_copy] > "r" (primask_copy)); > } > > private: > volatile int _primask; > } CProtect; > > and here's how it's used: > > { > CProtect protect; > > // critical code goes here > }
This works (with the optimize attribute specified for each function, and the level set at O0), but I would like some opinions on whether it is kosher. It works even when the overall optimization level is set to "O3", which is cool. typedef class CProtect { public: CProtect(void) __attribute__ ((__optimize__ ("O0"))) { int primask_copy; asm("mrs %[primask_copy], primask\n\t" // save interrupt status "cpsid i\n\t" // disable interrupts : [primask_copy] "=r" (primask_copy)); _primask = primask_copy; } ~CProtect() __attribute__ ((__optimize__ ("O0"))) { int primask_copy = _primask; // Restore interrupts to their previous value asm("msr primask, %[primask_copy]" : : [primask_copy] "r" (primask_copy)); } private: volatile int _primask; } CProtect; -- Tim Wescott Wescott Design Services http://www.wescottdesign.com

The 2024 Embedded Online Conference