Code Generation Error in WinAVR-20100110

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

I'm posting this error (I think, please prove me wrong) here since I don't know of another place, now that WinAVR development has been frozen. Hopefully someone will file this one for a future fix.

This test program fails because the reserved register r10 is used in the generated code (and not saved):

#include 

register unsigned char GYOffset asm("r10"); 

typedef unsigned char byte;
typedef unsigned int uint;

typedef struct TimeDate {
	byte	Day;
	byte	Month;
	byte	Year;
} TimeDate_t;

void ComputeLargeNum(TimeDate_t *p, char *pStr)
{
// Base date: August 1, 2010. No data before that date
	if ((p->Year < 10) || ((p->Year == 10) && (p->Month < 8))) {
		*pStr = 0;
		return;
	}
	// todo accurate computation needed
	uint nDays = (p->Year - 10) * 365 + (p->Month - 8) * 31 + (p->Day - 1);
	unsigned long long LargeNum = 13248524997010ULL + nDays * 4173424303;
	uint n = 0;
	while (LargeNum > 1000000000ULL) {
		++n;
		LargeNum -= 1000000000ULL;
	}
}

int main(void)
{
	TimeDate_t td;
	td.Year = 10;
	td.Month = 8;
	td.Day = 1;
	char buf[20];
	GYOffset = 1;
	ComputeLargeNum(&td, buf);
	if (GYOffset != 1) {
		snprintf(buf, 20, "r10 corrupted\n");
	}
}

The generated code (using AVR Studio with default options ) is attached. Just search for r10. You'll see it is assigned ONCE in the code generated for the large number computation, but never used.
In main(), r10 is used as variable GYOffset, and it's value changes after the function call.

jrseattle

Attachment(s): 

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

The attached text file WinAvrBug.txt is not readible because of strange formatting by this site. I'm attaching the file as a zip file. Hopefully that works.

jrseattle

Attachment(s): 

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

I don't have any problem with the text file as posted above. (WinAvrBug.txt)

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

1. what are your command line switches - I can't reproduce your problem

2. library functions do overwrite registers unless you recompile them with the register set aside. As an example, the whole printf() family corrupts all registers.

JW

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

Quote:

1. what are your command line switches - I can't reproduce your problem

I can - just the default project settings in Studio:

avr-gcc  -mmcu=atmega16 -Wall -std=gnu99 --save-temps -fverbose-asm    -DF_CPU=8000000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -gdwarf-2 -MD -MP -MT test.o -MF dep/test.o.d  -c  ../test.c

(well OK I added --save-temps and -fverbose-asm to that)

I then got:

  f8:	ab 2e       	mov	r10, r27
  fa:	a6 e6       	ldi	r26, 0x66	; 102
  fc:	ba 2e       	mov	r11, r26
  fe:	f1 ec       	ldi	r31, 0xC1	; 193
 100:	cf 2e       	mov	r12, r31
 102:	e8 ef       	ldi	r30, 0xF8	; 248
 104:	de 2e       	mov	r13, r30
 106:	ee 24       	eor	r14, r14
 108:	ff 24       	eor	r15, r15
 10a:	00 e0       	ldi	r16, 0x00	; 0
 10c:	10 e0       	ldi	r17, 0x00	; 0
 10e:	0e 94 d5 01 	call	0x3aa	; 0x3aa <__muldi3>

which uses r10 in the first instruction. However if you carry on down the .lss you get to:

 430:	59 01       	movw	r10, r18
 432:	6a 01       	movw	r12, r20

actually "inside" <__muldi3>:

So it seems that it's not only library functions but also "library operators" that may use r10

EDIT: The .map file says:

c:/winavr-20100110/bin/../lib/gcc/avr/4.3.3/avr5\libgcc.a(_muldi3.o)
                              test.o (__muldi3)

So the r10 using library code is coming from libgcc.a so that would need to be rebuilt with r10 not used.

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

Ok,

then now I'm curious as to why you would explicitely want to use a specific register.
Why not let the Compiler pick one?
where ther not a couple of registers free for use in the higher end ( like R24/R25 and upwards).

regards

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

Quote:

I'm curious as to why you would explicitely want to use a specific register.

It's usually so you can have a very fast interrupt in Asm. Something like:

vect:
    inc r10
    reti

EDIT: If it's just being used as a binary flag to signal whether an offset is applied in this case I'm not sure I understand the need for it to bound to a register - but maybe this is just a test case, not real code.

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

Hi All,

First, if anyone has any potential bug reports for WinAVR, then please send them to Atmel instead. Send them to avr AT atmel DOT com. Though posting them here first to make sure that it is a real bug is still preferable.

@Cliff: Sometimes doing a multiplication operation will then cause the compiler to substitute the operation with a library function, and this library function is in libgcc. However, if you take a look at the libgcc code (in GCC, in the AVR port code) you'll see that it is written in assembly. So one cannot really "recompile" the library to not use r10. One would have to patch the libgcc code to use a different register.

This is why it is dangerous to specify the exact register to use like in the test case. If you have to write inline assembly then it is better to let the compiler choose the register for you. Luckily GCC has a very powerful (but with that, very complicated) inline assembly capabilities that can usually do this sort of thing.

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

Eric,

Does linking with libm.a help here (maybe just as an alternative implementation that doesn't happen to use r10) or is it just transcendtal functions and not the base operators like long*long ?

Cliff

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

libm.a is the math library portion of the C standard library, so in this particular case it won't help. There are some AVR devices that do not have MUL instructions, so that's typically when libgcc comes into play. It provides a multiply library routine that is used for the simple multiply operator. Typically that code is not small, hence it's put into a called library routine. It's always instructive to take a look at the compiler-generated assembly.

BTW, this is not a new issue. This type of implementation has been around for as long as I can remember.

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

clawson wrote:
Eric,

Does linking with libm.a help here (maybe just as an alternative implementation that doesn't happen to use r10) or is it just transcendtal functions and not the base operators like long*long ?

Cliff


Cliff,

as with all library functions, you could provide your own implementation of __muldi3(); however, that would not help. This is a fundamental problem: __muldi3 is a long long * long long multiplication, i.e. it has 2x8 input bytes; and according to the ABI that inevitably uses up r10 (r10-r17 and r18-r25).

I'd suggest the OP to rewrite the function to explicitly multiply only longs. The main problem is IMHO in the 4173424303 constant, which IMHO implicitly converts to long long. However, I am not sure how to approach this correctly (except of rewriting the algorithm, which is always the best thing to start with).

Just a hint:

(NDays * 4173424303) / 1000000000 = 
(NDays * (4000000000 + 173424303)) / 1000000000 = 
(NDays * 4) + ((NDays * 173424303) / 1000000000)

etc. (homework ;-) )

Another thing to consider of course is the fixed register usage.

JW

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

Thanks everyone for the response. It's now clear that r10 is used as an argument to the __muldi3 library function.

That leaves me with 2 questions:

1. Why is r10 not saved in the function "ComputeLargeNum"? After all, r10 is a "call saved" register; all the other ones are saved (see code). Is it possible that winavr decides not to save it because it is explicitly designated a register variable by the user code, but then uses it, out of necessity, anyway?

	void ComputeLargeNum(TimeDate_t *p, char *pStr)
	{
	  be:	9f 92       	push	r9
	  c0:	bf 92       	push	r11
	  c2:	cf 92       	push	r12
	  c4:	df 92       	push	r13
	  c6:	ef 92       	push	r14
	  c8:	ff 92       	push	r15
	  ca:	0f 93       	push	r16
	  cc:	1f 93       	push	r17
	  ce:	cf 93       	push	r28
	  d0:	df 93       	push	r29
	

I realize, that, even if saved, r10 is not safe in interrupt functions.

2. Do the library functions like "printf" destroy "call saved" registers, as some of the comments suggest?

jrseattle

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

jrseattle wrote:
1. Is it possible that winavr decides not to save it because it is explicitly designated a register variable by the user code, but then uses it, out of necessity, anyway?

Yes, that's it.

avr-gcc might perhaps throw a warning in these cases (Eric, is it worth to submit a feature request? If so, where?)

jrseattle wrote:
2. Do the library functions like "printf" destroy "call saved" registers

I perhaps did not formulate properly above: while printf() and family does not destroy the call saved registers (including r10) as it is saving/restoring it in the common prologue/epilogue; it does use and modify these registers, thus it is not safe to use them in interrupts (which is presumably one of the major reasons to use register variables) even if the user assigned them to variables.

JW

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

wek wrote:

avr-gcc might perhaps throw a warning in these cases (Eric, is it worth to submit a feature request? If so, where?)

The best place for that is the GCC Bugzilla database here:
http://gcc.gnu.org/bugzilla/

Be sure to put "avr" in the "Target" field. The component field should be set to "target". And it should be marked as an "enhancement". Please put my email address in the CC field, that way I can track it.

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

Entered as bug 45099 on bugzilla as requested.

Thanks

jrseattlle