optimization problem

14 replies [Last post]
markr
Offline
Joined: 2013-07-19
Posts: 14

I've run into a problem that I believe is a compiler optimization problem. I'm using an LPC-812 on a breakout board that does not support either JTAG or SWD, so debugging this has been interesting. I'm using LPCXpresso v6.0.2 Build 151 2013-09-18. The code in question is:

...set-up a bunch of things, including clocks, ISR handles, pins etc, then...
while (1)                                /* Loop forever */
  {
	  while(!LPC_GPIO_PORT->W0[ISP_PIN]) ; // wait on ISP button press
	  while (TimeTick <= change_pwm); //wait for the counter to advance
	  TimeTick=0;
#ifdef DBG_OUTPUT
	  printf("R=%3d, G=%3d, B=%3d\n\r",red_pwm,green_pwm,blue_pwm);
#endif
	  NOTPORT0(PORT0_BIT6); // toggle pin
//	  printf("%2d",color);
	  switch (color) {
... several cases: with breaks, a default, then end of the while(1).

All code from the switch() on-wards does not appear to be getting executed. The switch has several cases, each case has several tests and modifies global variables that are used in ISRs. The end of the switch is the end of the while(1).

If either printf() shown above is enabled (either enable the #ifdef or remove the comment), the switch operates correctly. If neither one is present, the code in the switch is never executed. If a printf() is placed in _any_ of the case statements, the switch executes and all cases operate correctly.

I know the processor is not off in the weeds because:
- There are pin toggles in 2 timer ISRs that keep toggling.
- The NOTPORT0 toggles a pin and that pin toggles correctly, so the primary while loop appears to be functioning correctly.
- The pin toggled by the NOTPORT0 stops toggling when the ISP button is pressed (as would be expected) and the ISRs keep running (as also expected and evidenced by pin toggles). And the pin controlled by the NOTPORT0 resumes toggling when the button is released (again as expected.)

Everything else appears to be operating correctly, just the entire switch structure is never executed! I chased this for most of a day (debugging w/o a debugger is fun!) until I started changing the optimization.

The project was initially set with size optimization (-Os) I changed to none (-O0), cleaned and rebuilt and it worked. Change it back to size (-Os), clean and rebuild and it doesn't work. There is definitely something bad going on with optimization. (I haven't tried the other levels yet.)

Besides the problem noted above, it appears that just changing the optimization level does not cause a re-build of the project. The code remains working or not working, irrespective of the optimization setting. Only if I change the optimization _and_ touch the code (add/delete a space and save) and recompile, does the behavior change. A clean and rebuilt does generate new code and will cause it to work or not work, depending on the state of the code and optimization.

The need to clean and rebuilt when changing optimization is not that surprising and is a minor nuisance. But having optimization remove most of main() is another issue.

I'm new to LPCXpresso and ARM in general, but have been using other micro-controlllers and IDEs for years. I'll admit I don't know LPCXpresso well enough to generate assembly and look at the resulting intermediate code. (It took some wrangling to get it to output a hex file for Flash Magic.)

Any thoughts or suggestions would be appreciated.

-markr

0
Your rating: None

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
R2D2's picture
R2D2
Offline
Joined: 2013-04-09
Posts: 662

Is 'color' volatile?

--------------------------------------------------
-- May the Force be with you --

--------------------------------------------------
-- May the Force be with you --

markr
Offline
Joined: 2013-07-19
Posts: 14

R2D2 wrote:
Is 'color' volatile?

no. it's an unsigned int only used in main. It's basically the "state" of the system: its incremented in every case statement, so the next pass around the while loop goes to the next state/switch-case.

You're onto an interesting idea. I'll try changing it.

I wouldn't expect an optimizer would discard an entire switch structure just because the switch state is only modified inside the switch. Makes state machines hard to implement in SW. Smile

-markr

R2D2's picture
R2D2
Offline
Joined: 2013-04-09
Posts: 662

markr wrote:
I wouldn't expect an optimizer would discard an entire switch structure...

I would Wink

http://www.support.code-red-tech.com/CodeRedWiki/CompilerOptimization?highlight=%28volatile%29

--------------------------------------------------
-- May the Force be with you --

--------------------------------------------------
-- May the Force be with you --

markr
Offline
Joined: 2013-07-19
Posts: 14

Looking at the link, all I can say is wow. I've used CCS for TI, GCC for Atmel and MicroChip and IAR for all 3 and never seen anything like this. Sure, the usual problems were a while loop that increments a dummy variable vanishes, or occasionally a repeated increment to a memory mapped I/O may disappear when optimization is turned up. But nothing like this.

Unfortunately, marking color volatile didn't fix the problem. Same behavior. And there were already variables inside the switch that were marked volatile. The only thing that fixes this is turning off _all_ optimization. Even -O1 fails. I'll retry this tomorrow on another vendor's compiler with the same code and see what happens.

I've included the entire switch code below:
- The variables "red_pwm", "blue_pwm" & "green_pwm" were already volatile.
- Adding a pin I/O inside the switch and/or cases didn't help.
- Making color volatile didn't help.
It only works correctly is optimization is turned off completely.

This leads to another question: How to mark I/O as volatile when all the register structures are already defined in library includes? Especially on a processor like ARM where _all_ I/O is memory mapped?

For example, I'm using a macro to access the I/O ports:
(I give up, the forum editor eats the example. Its a define using the standard LPC I-O structure)
If the problem is memory mapped I/O, shouldn't _all_ I/O be marked as volatile in the libraries? (hint: it's not)

Any other ideas? This isn't the end of the world, I'm just trying to educate myself on ARM in general and NXP in particular. The LPC8xx looked like a good option for high performance in a small package and low price. It would be nice to use the "house brand" compiler, but problems like this are annoying. I'm glad this isn't a memory limited project on a deadline....

-markr

markr
Offline
Joined: 2013-07-19
Posts: 14

The code mentioned above is below.

-markr

while (1)                                /* Loop forever */
  {
	  while(!LPC_GPIO_PORT->W0[ISP_PIN]) ;	// wait if ISP button pressed
	  while (!TimeTick);
	  TimeTick=0;
#ifdef DBG_OUTPUT
	  printf("R=%3d, G=%3d, B=%3d\n\r",red_pwm,green_pwm,blue_pwm);
#endif
//	  NOTPORT0(PORT0_BIT6);
//	  printf("%2d",color);
	  switch (color) {
	  case 0: //red
		  if (inc_dec) {
	   		if (++red_pwm >= MAX_PWM) inc_dec = FALSE;
	   	}
	   	else if (--red_pwm == 0) {
	   		color++;
	   		inc_dec = TRUE;
	   	}
	   	break;
	  case 1: //green
		  if (inc_dec) {
		  	if (++green_pwm >= MAX_PWM) inc_dec = FALSE;
		  }
		  else if (--green_pwm == 0) {
		  	color++;
		  	inc_dec = TRUE;
		  }
		  break;
	  case 2:	// blue
		  if (inc_dec) {
		  	if (++blue_pwm >= MAX_PWM) inc_dec = FALSE;
		  }
		  else if (--blue_pwm == 0) {
		  	color++;
		  	inc_dec = TRUE;
		  }
		  break;
	  case 3:	// purple
		  if (inc_dec) {
			blue_pwm++;
			red_pwm++;
		  	if (blue_pwm >= MAX_PWM) inc_dec = FALSE;
		  }
		  else {
			blue_pwm--;
			red_pwm--;
		  	if (blue_pwm == 0) {
		  		color++;
		  		inc_dec = TRUE;
		  	}
		  }
		  break;
	  case 4:	// yellow?
		  if (inc_dec) {
			green_pwm++;
			red_pwm++;
		  	if (green_pwm >= MAX_PWM) inc_dec = FALSE;
		  }
		  else {
			green_pwm--;
			red_pwm--;
		  	if (green_pwm == 0) {
		  		color++;
		  		inc_dec = TRUE;
		  	}
		  }
		  break;
	  case 5:	// orange? blue is too weak
		  if (inc_dec) {
			green_pwm++;
			blue_pwm++;
		  	if (green_pwm >= MAX_PWM) inc_dec = FALSE;
		  }
		  else {
			green_pwm--;
			blue_pwm--;
		  	if (green_pwm == 0) {
		  		color++;
		  		inc_dec = TRUE;
		  	}
		  }
		  break;
	  case 6:	// white
		  if (inc_dec) {
			red_pwm++;
			green_pwm++;
			blue_pwm++;
		  	if (green_pwm >= MAX_PWM) inc_dec = FALSE;
		  }
		  else {
			red_pwm--;
			green_pwm--;
			blue_pwm--;
		  	if (green_pwm == 0) {
		  		color++;
		  		inc_dec = TRUE;
		  	}
		  }
		  break;
	  default:	// clean up (should not be necessary)
		  color = 0;
		  inc_dec = TRUE;
		  red_pwm=0;
		  green_pwm=0;
		  blue_pwm=0;
	  }
	}

TheFallGuy
Offline
Joined: 2012-03-28
Posts: 884

I would suggest that you need to debug this. You said that 'it appears the switch statement is not being executed' but you don't actually know until you debug it. I would suggest
- in the Release build add the -g3 flag (Maximum debug info)
- disassemble your application so you can see what the code is
- single step through your code to find out what is really not working.
It is not easy debugging optimized code (all of the reasons explained the the FAQ you have been pointed at) but you will need to do it. These GCC compilers are very mature, so it is very unlikely to be a compiler problem.

We can speculate until the cows come home, but that is all we can do - speculate.

Regarding use of volatile - the compiler is doing what every other ARM compiler will do when optimizing. It is very very different to the 8/16 bit compilers.

wmues
Offline
Joined: 2011-08-16
Posts: 155

Markr,

if color is used only inside main(), there is no need to make color volatile.

But you have 2 while loops at the start of the loop.
- is TimeTick volatile?
- Is LPC_GPIO_PORT->W0[ISP_PIN] volatile?

If not, the compiler might assume that one of those variables is 0 and not changing value, so the rest of the loop is optimized away (unreachable code).

regards
Wolfgang

Dore
Offline
Joined: 2013-09-26
Posts: 17

Hi,

I am running short of memory in my controller. So I have enabled the optimization level 3 (Release mode) in my LPCXpresso. I am using LPC1114FHI33/302. With this one my code is fitting into the memory. But I will have to look into few concerns.

- Is there any special care i have to takecare since I am enabling the optimization?
- Is ok to keep the compiler optimization technique?
- Any inputs on what exactly the optimization does and is it possible for me to take care of them so that I can go without any optimization?

Regards,
Duraimurugan

lpcxpresso-support's picture
lpcxpresso-support
Offline
Joined: 2013-06-05
Posts: 1636

First of all, if you are worried about the size of your code, DO NOT use -O3 as it will increase code size (compared with -Os or -O2). This is because -O3 optimization is to do with speed and not size. For example, it will unroll small loops, making the code run faster, but at the cost of increasing the size.

Therefore, I suggest you use -O2 or -Os.

Also, please read this FAQ:
http://support.code-red-tech.com/CodeRedWiki/CompilerOptimization

---
** Latest version of LPCXpresso IDE : v7.5.0 **
http://www.lpcware.com/content/forum/lpcxpresso-latest-release

Visit the LPCXpresso 6 & later FAQs at http://www.lpcware.com/faq/lpcxpresso
Visit the LPCXpresso 5 FAQs at http://support.code-red-tech.com/CodeRedWiki

---
** Latest version of LPCXpresso IDE : v7.5.0 **
http://www.lpcware.com/content/forum/lpcxpresso-latest-release

Visit the LPCXpresso 6 & later FAQs at http://www.lpcware.com/faq/lpcxpresso
Visit the LPCXpresso 5 FAQs at http://support.code-red-tech.com/CodeRedWiki

rocketdawg
Offline
Joined: 2012-06-13
Posts: 161

consider an accessor function to get the variable "color"

uint32_t color;
uint32_t GetColor(void)
{
return color;
}

switch(getColor()) { blah blah}

"volatile" may or may not work, (depends on the compiler) but the compiler cannot optimize out a function call.

Dore
Offline
Joined: 2013-09-26
Posts: 17

Is it recommended to use -Os or -O2 (in release mode)? will there be any issues due to this which can affect the actual functionality?
And also if I would like to make the optimization on my own equivalent to the one on O2 or Os setting, how I can move forward on this?

LabRat's picture
LabRat
Offline
Joined: 2013-11-04
Posts: 483

Duraimurugan wrote:
Is it recommended to use ...

It is recommended to use Google:

http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html

lpcxpresso-support's picture
lpcxpresso-support
Offline
Joined: 2013-06-05
Posts: 1636

You can use -O2 or -Os - it is your choice.

Provided you obey the rules (as described in the FAQ that I provided a link to) there will be no issues due to optimization.

Read the Help (Help->Help contents) for information on all of the optimzations available and which ones are used at each level. Beware: this is for very advanced users only.

---
** Latest version of LPCXpresso IDE : v7.5.0 **
http://www.lpcware.com/content/forum/lpcxpresso-latest-release

Visit the LPCXpresso 6 & later FAQs at http://www.lpcware.com/faq/lpcxpresso
Visit the LPCXpresso 5 FAQs at http://support.code-red-tech.com/CodeRedWiki

---
** Latest version of LPCXpresso IDE : v7.5.0 **
http://www.lpcware.com/content/forum/lpcxpresso-latest-release

Visit the LPCXpresso 6 & later FAQs at http://www.lpcware.com/faq/lpcxpresso
Visit the LPCXpresso 5 FAQs at http://support.code-red-tech.com/CodeRedWiki

R2D2's picture
R2D2
Offline
Joined: 2013-04-09
Posts: 662

In general O2 is the better (=faster) choice. If you are short of flash, use Os (= LPCXpresso default release setting).

--------------------------------------------------
-- May the Force be with you --

--------------------------------------------------
-- May the Force be with you --

feedback