Compiler output incorrect?

Go To Last Post
2 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quick background: I've been having a problem with the NVM controller getting a calibration value out of the production signature row on an XMega32AU. The function was only returning the lower 8 bits of the 16 bit value. There was a lot of peripherals, assembly and ASF code involved, but I finally distilled the problem down to the assembly output of the compiler not being correct.

I'm not qualified to say it's a compiler bug, but it looks that way to me.

Here's my distilled test code without anything else going on:

#include 

uint16_t global;

uint8_t function_a(uint8_t param)
{
	return 0xAA;
}

static inline uint16_t function_b(void)
{
	uint16_t data;

	data = function_a(0);
	data = data << 8;
	data |= function_a(1);

	return data;
}

void do_stuff(uint16_t *ptr)
{
	*ptr = function_b();
}

int main(void)
{
	do_stuff(&global);
	
	// global should be 0xAAAA
	// global is 0x00AA
	
	while(1);
}

Build commands (from AS6):

"C:\Program Files (x86)\Atmel\Atmel Studio 6.0\extensions\Atmel\AVRGCC\3.3.2.31\AVRToolchain\bin\avr-gcc.exe"  -funsigned-char -funsigned-bitfields -O1 -fpack-struct -fshort-enums -g2 -Wall -c -std=gnu99 -MD -MP -MF "gcctest.d" -MT"gcctest.d"  -mmcu=atmega168  -o"gcctest.o" ".././gcctest.c"

"C:\Program Files (x86)\Atmel\Atmel Studio 6.0\extensions\Atmel\AVRGCC\3.3.2.31\AVRToolchain\bin\avr-gcc.exe" -o gcctest.elf  gcctest.o   -Wl,-Map="gcctest.map" -Wl,--start-group -Wl,-lm  -Wl,--end-group  -mmcu=atmega168 

I changed to atmega168 to verify it wasn't anything xmega related. Problem exists in both cases.

Output of --version:

C:\Users\Jeffrey Nichols\Documents>"C:\Program Files (x86)\Atmel\Atmel Studio 6.
0\extensions\Atmel\AVRGCC\3.3.2.31\AVRToolchain\bin\avr-gcc.exe" --version
avr-gcc.exe (AVR_8_bit_GNU_Toolchain_3.3.2_485) 4.5.1
Copyright (C) 2010 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

The relevant sections of the .lss file:

00000090 :
uint16_t global;

uint8_t function_a(uint8_t param)
{
	return 0xAA;
}
  90:	8a ea       	ldi	r24, 0xAA	; 170
  92:	08 95       	ret

00000094 :

	return data;
}

void do_stuff(uint16_t *ptr)
{
  94:	ef 92       	push	r14
  96:	ff 92       	push	r15
  98:	0f 93       	push	r16
  9a:	1f 93       	push	r17
  9c:	cf 93       	push	r28
  9e:	df 93       	push	r29
  a0:	8c 01       	movw	r16, r24

static inline uint16_t function_b(void)
{
	uint16_t data;

	data = function_a(0);
  a2:	80 e0       	ldi	r24, 0x00	; 0
  a4:	0e 94 48 00 	call	0x90	; 0x90 
	data = data << 8;
  a8:	e0 e0       	ldi	r30, 0x00	; 0
  aa:	ef 01       	movw	r28, r30
	data |= function_a(1);
  ac:	81 e0       	ldi	r24, 0x01	; 1
  ae:	0e 94 48 00 	call	0x90	; 0x90 
  b2:	90 e0       	ldi	r25, 0x00	; 0
  b4:	c8 2b       	or	r28, r24
  b6:	d9 2b       	or	r29, r25
	return data;
}

void do_stuff(uint16_t *ptr)
{
	*ptr = function_b();
  b8:	f8 01       	movw	r30, r16
  ba:	d1 83       	std	Z+1, r29	; 0x01
  bc:	c0 83       	st	Z, r28
}
  be:	df 91       	pop	r29
  c0:	cf 91       	pop	r28
  c2:	1f 91       	pop	r17
  c4:	0f 91       	pop	r16
  c6:	ff 90       	pop	r15
  c8:	ef 90       	pop	r14
  ca:	08 95       	ret

000000cc 
: int main(void) { do_stuff(&global); cc: 80 e0 ldi r24, 0x00 ; 0 ce: 91 e0 ldi r25, 0x01 ; 1 d0: 0e 94 4a 00 call 0x94 ; 0x94 d4: ff cf rjmp .-2 ; 0xd4 000000d6 <_exit>: d6: f8 94 cli 000000d8 <__stop_program>: d8: ff cf rjmp .-2 ; 0xd8 <__stop_program>

The problem is between the two calls to function_a, where the compiler ignores the data returned in r24. This leaves "global" as 0x00AA instead of the expected 0xAAAA.

This happens on -01 and not -O0, but running my original program on -O0 is not a realistic solution.

Does anyone have any idea why this is happening, and more importantly, how I can avoid it in the future?

Jeff Nichols

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Looks very much like like nasty PR46779, fixed in 4.6.2.

Sometimes -fno-split-wide-types helps but it's no guarantee.

Other work around could be to obfuscate what function_b is doing, e.g. by performing

a | (b << 8)

by means of inline assembly.

However, the only secure solution is to use a toolchain that comes with PR46779 fixed. Or, at your option, compile it yourself from your toolchain's sources (you have the sources because you have avr-gcc binary and GCC is GPL) and backport
http://gcc.gnu.org/viewcvs?view=...

avrfreaks does not support Opera. Profile inactive.